Удалить все, кроме первого вхождения символа в списке строк - PullRequest
0 голосов
/ 06 июня 2018

У меня есть список имен, и мне нужно вывести одну строку, которая показывает буквы из имен в порядке их появления без дубликатов (например, если список равен ["John"; "James"; "Jack"], строка вывода должна быть Johnamesck).У меня есть решение (сворачивать все имена в строку, а затем анализировать), но я чувствую, что я немного ее проверяю, делая свою строку изменчивой.

Я также хочу заявить, что это не школьное задание, а упражнение от коллеги по работе, поскольку я вхожу в F # только из тех, кто когда-либо знал, что такое Java Web.

Вот моя работарешение (для ознакомления):

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 - это просто то, что я не отпускаю свои императивные привычки (как я читал, это редко нужно использовать), и я хочу усилить чистый функционал.Так есть ли лучший способ сделать это?Является ли второе решение возможным путем, если я могу каким-то образом передать результат куда-нибудь?

Ответы [ 2 ]

0 голосов
/ 06 июня 2018

Вы можете использовать Seq.distinct для удаления дубликатов и сохранения порядка, поэтому вам просто нужно преобразовать список строк в одну строку, что можно сделать с помощью String.concat "":

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

Если вы запустите distinctChars ["John"; "James"; "Jack"], вы получите обратно:

"Johnamesck"
0 голосов
/ 06 июня 2018

Это должно сработать:

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

По сути, мы просто рассматриваем список строк как одну большую последовательность символов и выбираем те, в которых индекс в общей последовательности совпадает с индексоминдекс первого вхождения этого символа.

Running removeDuplicateCharacters ["John"; "James"; "Jack";] дает ожидаемый результат: "Johnamesck".

...