删除字符串列表中的所有字符,但首次出现

问题描述 投票:6回答:2

我有一个名单列表,我需要输出一个字符串,按照它们出现的顺序显示名称中的字母而不重复(例如,如果列表是["John"; "James"; "Jack"],输出字符串应该是Johnamesck)。我有一个解决方案(将所有名称折叠成一个字符串,然后解析),但我觉得我通过使我的字符串变得可变来讨厌它。

我还想说这不是一个学校作业,只是一个工作同事的练习,因为我从只知道Java Web的东西进入F#。

这是我的工作解决方案(用于洞察目的):

let lower = ['a' .. 'z']
let upper = ['A' .. 'Z']
let mutable concatedNames = ["John"; "James"; "Jack"] |> List.fold (+) ""

let greaterThanOne (length : int) = length > 1
let stripChars (str : string) letter =
    let parts = str.Split([| letter |])
    match greaterThanOne (Array.length parts) with
    | true -> seq {
                    yield Array.head parts
                    yield string letter
                    yield! Array.tail parts
                  }
                  |> String.concat ""
    | _ -> str

let killAllButFirstLower = lower |> List.iter (fun letter -> concatedNames <- (stripChars concatedNames letter))
let killAllButFirstUpper = upper |> List.iter ( fun letter -> concatedNames <- (stripChars concatedNames letter))
printfn "All names with duplicate letters removed: %s" concatedNames

我本来想单独用函数明确地做这个,并且在上面有一个解决方案

let lower = ['a' .. 'z']
let upper = ['A' .. 'Z']
:
:
:
let lowerStripped = [""]
let killLowerDuplicates = lower |> List.iter (fun letter -> 
                                        match lowerStripped.Length with
                                        | 1 ->
                                                (stripChars concatedNames letter)::lowerStripped |> ignore

                                        | _ ->  (stripChars (List.head lowerStripped) letter)::lowerStripped |> ignore

                                 )

let upperStripped = [List.head lowerStripped]
let killUpperDuplicates = lower |> List.iter ( fun letter -> (stripChars (List.head upperStripped) letter)::upperStripped |> ignore )
let strippedAll = List.head upperStripped
printfn "%s" strippedAll

但我无法让这个工作,因为我意识到consed列表不会去任何地方(更不用说这可能是低效的)。这个想法是通过这样做,一旦我解析了所有内容,列表的第一个元素就是所需的字符串。

我明白问一个我已经有解决方案的问题可能很奇怪,但我觉得使用mutable只是我不放弃我的势在必行的习惯(因为我读过它应该很少需要使用它)和我想要更强化纯粹的功能。那么有更好的方法吗?如果我能以某种方式将结果传递到某个地方,第二个解决方案是否可行?

f# functional-programming
2个回答
9
投票

您可以使用Seq.distinct删除重复项并保留排序,因此您只需将字符串列表转换为单个字符串,这可以使用String.concat ""完成:

let distinctChars s = s |> String.concat ""
                        |> Seq.distinct
                        |> Array.ofSeq
                        |> System.String

如果你运行distinctChars ["John"; "James"; "Jack"],你会回来:

"Johnamesck"

5
投票

这应该做的伎俩:

let removeDuplicateCharacters strings =
    // Treat each string as a seq<char>, flattening them into one big seq<char> 
    let chars = strings |> Seq.collect id  // The id function (f(x) = x) is built in to F#
                                           // We use it here because we want to collect the characters themselves
    chars
    |> Seq.mapi (fun i c -> i,c) // Get the index of each character in the overall sequence
    |> Seq.choose (fun (i,c) ->  
        if i = (chars |> Seq.findIndex ((=) c))  // Is this character's index the same as the first occurence's index?
        then Some c                              // If so, return (Some c) so that `choose` will include it,
        else None)                               // Otherwise, return None so that `choose` will ignore it
    |> Seq.toArray // Convert the seq<char> into a char []
    |> System.String // Call the new String(char []) constructor with the choosen characters

基本上,我们只是将字符串列表视为一个大字符序列,并选择整个序列中的索引与该字符第一次出现的索引相同的字符串。

运行removeDuplicateCharacters ["John"; "James"; "Jack";]给出了预期的输出:"Johnamesck"

© www.soinside.com 2019 - 2024. All rights reserved.