Bre*_*rry 3 .net string collections f#
我想使用牛津(或串行)逗号将字符串集合加入单个字符串中。
给定
let ss = [ "a"; "b"; "c"; "d" ]
Run Code Online (Sandbox Code Playgroud)
我想要
"a, b, c, and d"
Run Code Online (Sandbox Code Playgroud)
这是我想出的。
let oxford (strings: seq<string>) =
let ss = Seq.toArray strings
match ss.Length with
| 0 -> ""
| 1 -> ss.[0]
| 2 -> sprintf "%s and %s" ss.[0] ss.[1]
| _ ->
let allButLast = ss.[0 .. ss.Length - 2]
let commaSeparated = System.String.Join(", ", allButLast)
sprintf "%s, and %s" commaSeparated (Seq.last ss)
Run Code Online (Sandbox Code Playgroud)
如何改善呢?
---编辑---
关于多次遍历序列的注释就很重要了。下面的两种实现都避免了转换为数组。
如果使用seq
,我会很喜欢:
open System.Linq
let oxfordSeq (ss: seq<string>) =
match ss.Count() with
| 0 -> ""
| 1 -> ss.First()
| 2 -> sprintf "%s and %s" (ss.ElementAt(0)) (ss.ElementAt(1))
| _ ->
let allButLast = ss.Take(ss.Count() - 1)
let commaSeparated = System.String.Join(", ", allButLast)
sprintf "%s, and %s" commaSeparated (ss.Last())
Run Code Online (Sandbox Code Playgroud)
如果使用array
,我还可以通过利用索引来避免Last()的迭代。
let oxfordArray (ss: string[]) =
match ss.Length with
| 0 -> ""
| 1 -> ss.[0]
| 2 -> sprintf "%s and %s" ss.[0] ss.[1]
| _ ->
let allButLast = ss.[0 .. ss.Length - 2]
let commaSeparated = System.String.Join(", ", allButLast)
sprintf "%s, and %s" commaSeparated (ss.[ss.Length - 1]
Run Code Online (Sandbox Code Playgroud)
---编辑---
看到来自@CaringDev的链接,我认为这很好。没有通配符,处理空值,减少索引编制的正确性,并且仅在Join()方法中遍历数组一次。
let oxford = function
| null | [||] -> ""
| [| a |] -> a
| [| a; b |] -> sprintf "%s and %s" a b
| ss ->
let allButLast = System.ArraySegment(ss, 0, ss.Length - 1)
let sb = System.Text.StringBuilder()
System.String.Join(", ", allButLast) |> sb.Append |> ignore
", and " + ss.[ss.Length - 1] |> sb.Append |> ignore
string sb
Run Code Online (Sandbox Code Playgroud)
这曾经也很不错,甚至跳来跳去:
let oxford2 = function
| null | [||] -> ""
| [| a |] -> a
| [| a; b |] -> sprintf "%s and %s" a b
| ss ->
let sb = System.Text.StringBuilder()
let action i (s: string) : unit =
if i < ss.Length - 1
then
sb.Append s |> ignore
sb.Append ", " |> ignore
else
sb.Append "and " |> ignore
sb.Append s |> ignore
Array.iteri action ss
string sb
Run Code Online (Sandbox Code Playgroud)
您可以直接查看列表,并在列表上使用模式匹配。也许可以改善这一点,但它给出了想法。
let rec oxford (s:string) (ss:string list) =
match ss with
| [] -> s
| [x;y] -> sprintf "%s, %s, and %s" s x y
| h::t when String.length s = 0 -> oxford h t
| h::t -> oxford (sprintf "%s, %s" s h) t
Run Code Online (Sandbox Code Playgroud)
它使用较小的列表递归地调用自己,并使用逗号。当列表的大小仅为2时,它将使用和。when
不幸的是,但是发现我第一次打电话时需要一个空字符串,所以不要以,结尾。
编辑
因此,就我个人而言,我希望上面的选项适用于少量单词。但是,每次调用的字符串concat对于较大的数字而言效果都不理想。
// collect into a list including the *,* and *and*, then just concat that to string
let oxfordDrct (ss:string list) =
let l = ss |> List.length
let map i s = if(i < l-1) then [s;", "] else ["and ";s]
match ss with
| [] -> ""
| [x] -> x
| [x;y] -> sprintf "%s, and %s" x y
| _ -> ss |> List.mapi map |> List.concat |> String.concat ""
// Recursive like the original but instead pass a StringBuilder instead of string
let oxfordSb xs =
let rec collect (s:StringBuilder) (ss:string list) =
match ss with
| [] -> s
| [x;y] -> sprintf ", %s, and %s" x y |> s.Append
| h::t when s.Length = 0 -> collect (s.Append(h)) t
| h::t -> collect (s.Append(sprintf ", %s" h)) t
let sb = new StringBuilder()
(collect sb xs) |> string
Run Code Online (Sandbox Code Playgroud)
这两个选项的性能与原始选项非常相似,所有这些都比rec
by 更好string
。
归档时间: |
|
查看次数: |
163 次 |
最近记录: |