我有一个名单列表,我需要输出一个字符串,按照它们出现的顺序显示名称中的字母而不重复(例如,如果列表为["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
但是我无法解决这个问题,因为我意识到这些列表并没有发生在任何地方(更不用说这可能是低效的)。这个想法是通过这样做,一旦我解析了所有内容,列表的第一个元素就是所需的字符串。
我理解提问我已经有解决方案可能有点奇怪,但我觉得使用mutable
只是因为我没有放弃我的命令性习惯(因为我已经读过它应该是很少需要使用它)我希望更强化纯功能。那么有更好的方法吗?如果我能以某种方式将结果传递到某个地方,第二个解决方案是否可行?
答案 0 :(得分:9)
您可以使用Seq.distinct
删除重复项并保留排序,因此您只需要将字符串列表转换为单个字符串,这可以通过String.concat ""
完成:
let distinctChars s = s |> String.concat ""
|> Seq.distinct
|> Array.ofSeq
|> System.String
如果您运行distinctChars ["John"; "James"; "Jack"]
,您将会回来:
"Johnamesck"
答案 1 :(得分: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"
。