29

一个可能更像是咆哮的快速问题(但我希望得到启发)。

在 F# 中,字符串与 Seq 兼容,因此 "abcd" |> Seq.map f 将适用于字符串。

这是处理字符串的绝佳工具,例如从字符串中获取前 5 个字符:

"abcdef01234567" |> Seq.take 5

或删除重复字符:

"abcdeeeeeee" |> Seq.distinct

问题是一旦你得到 char seq 结果,再次将其转换回字符串变得非常尴尬, String.concat "" 要求成员是字符串,所以我最终做了很多:

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

如此之多,以至于我在 90% 的项目中都使用了一个函数:

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

我觉得这太过分了,但是在我寻找替代方案的任何地方,我都会遇到令人发指的事情,例如 StringBuilder 或内联 lambda 并使用 new:

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

我希望在语言中看到的(也许是疯狂的)期望是,当 Seq 用于字符串时,结果表达式的类型签名应该是字符串 -> 字符串。意思是进去的就是出来的。"abcd" |> Seq.take 3 = "abc"。

在这种情况下,我对高级字符串操作的期望是错误的吗?

有没有人建议以一种很好的方式解决这个问题,我觉得我一定错过了一些东西。

4

4 回答 4

28

我自己只是在研究这个。我发现 System.String.Concat 工作得很好,例如

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

假设你已经打开System.

于 2014-05-09T19:22:47.090 回答
12

模块中的函数Seq只处理序列——即,当你用 a 调用它们时string,它们只“看到” aSeq<char>并相应地对其进行操作。即使他们进行了特殊检查以查看参数是否为 astring并采取了一些特殊操作(例如,仅针对字符串的函数的优化版本),他们仍然必须将其作为 a 返回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
于 2013-02-03T00:13:14.220 回答
7

F# 有一个String 模块,其中包含一些Seq专门用于字符串的模块功能。

于 2013-02-03T00:37:22.470 回答
5

自从 5 年前提出这个问题以来,F# 已经获得了将构造函数用作函数的能力。我会使用String(Char[])将字符转换为字符串。您可以在 F# 序列或 F# 列表之间进行转换,但我也可能只使用使用String.ToCharArray方法的 F# 数组模块。

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

如果您真的想使用 achar seq那么您可以将其通过管道传递给 String ,如下所示:

printfn "%s" ("abcdef01234567" |> Seq.take 5 |> Array.ofSeq |> String)
于 2018-03-13T16:56:22.577 回答