F#, char seq -> строки

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

в F# строка совместима с Seq таким образом, что" abcd " | > Seq.отображение F будет работать на строке.

это блестящее средство для работы со строками, например, чтобы взять первые 5 символов из строки:

"abcdef01234567" |> Seq.take 5

или удаление повторяющихся символов:

"abcdeeeeeee" |> Seq.distinct

проблема в том, что как только у вас есть результат char seq, он становится чрезвычайно неудобно снова преобразовывать это в строку, строку.concat "" требует, чтобы члены были строками, поэтому я в конечном итоге делаю это много:

"abcdef01234567" 
|> Seq.take 5
|> Seq.map string
|> String.concat ""

настолько, что у меня есть функция, которую я использую в 90% моих проектов:

let toString : char seq -> string = Seq.map string >> String.concat ""

Я чувствую, что это над вершиной, но везде, где я ищу альтернативу, я встречаюсь с отвратительными вещами, такими как StringBuilder или inlining лямбда и использование нового:

"abcdef01234567" 
|> Seq.take 5
|> Seq.toArray 
|> fun cs -> new string (cs) (* note you cannot just |> string *)

мое (возможно, сумасшедшее) ожидание, которое я хотел бы видеть в язык заключается в том, что когда Seq используется в string, сигнатура типа из результирующего выражения должна быть string -> string. То есть, что входит, то и выходит. "abcd" / > Seq.возьмите 3 = "abc".

есть ли причина, по которой мои ожидания от высокоуровневой манипуляции строками ошибочны в этом случае?

есть ли у кого-нибудь рекомендация подойти к этому в хорошей манере, Я чувствую, что должен что-то упустить.

4 ответов


Я только что исследовал это сам. Я нашел эту систему.Строка.Конкат работает довольно хорошо, например

"abcdef01234567" |> Seq.take 5 |> String.Concat;;

предположим, что вы открыли System.


функции Seq модуль интернет только с последовательностями, т. е. когда вы звоните им с string, они только "видят" a Seq<char> и работайте на нем соответственно. Даже если они сделали специальную проверку, чтобы увидеть, был ли аргумент string и предпринял некоторые специальные действия (например, оптимизированную версию функции только для строк), они все равно должны были бы вернуть ее как Seq<char> чтобы успокоить систему типов F# - в этом случае вам нужно будет проверить возвращаемое значение везде, чтобы увидеть, было ли это на самом деле string.

хорошей новостью является то, что F# имеет встроенные ярлыки для некоторых из кода, который вы пишете. Например:

"abcdef01234567" |> Seq.take 5

можно сократить до:

"abcdef01234567".[..4]  // Returns the first _5_ characters (indices 0-4).

некоторые из других вам все равно придется использовать Seq Хотя, или напишите свою собственную оптимизированную реализацию для работы со строками.

вот функция для получения отдельных символов в строке:

open System.Collections.Generic

let distinctChars str =
    let chars = HashSet ()
    let len = String.length str
    for i = 0 to len - 1 do
        chars.Add str.[i] |> ignore
    chars

F# имеет модуль строки и Seq функциональность модуля специализированная для строк.


F# получил возможность использовать конструкторы в качестве функций, так как этот вопрос был задан 5 лет назад. Я бы использовал Строки(Char[]) для преобразования символов в строку. Вы можете конвертировать в и из последовательности F# или списка F#, но я бы, вероятно, просто использовал модуль массива F#, используя строку.ToCharArray метод слишком.

printfn "%s" ("abcdef01234567".ToCharArray() |> Array.take 5 |> String)

Если вы действительно хотели использовать char seq затем вы можете передать его в строку следующим образом:

printfn "%s" ("abcdef01234567" |> Seq.take 5 |> Array.ofSeq |> String)