我有一个在特定行号上有硬中断的行列表,如下所示:
Header:<SmallJson>
Header:<VeryLongJson...
....>
Header:<Json>
我需要处理它以便删除换行符,所以它变成这样:
Header:<SmallJson>
Header:<VeryLongJson.......>
Header:<Json>
我想出了一个解决方案,但我并不是特别高兴:
let rawLines = [ "Header:<SmallJson>"
"Header:<VeryLongJson..."
"....>"
"Header:<Json>" ]
let processedLines =
(([], ""), rawLines)
||> List.fold (fun (result, state) line ->
if line.StartsWith "Header:"
then state::result, line
else result, state + line)
|> (fun (result, state) -> state::result)
|> List.rev
|> List.tail
有没有办法以更简单的方式实现这一目标?折叠结束时的额外state::result
和List.tail特别惹恼了我。优选不使用突变
答案 0 :(得分:3)
这基本上是“分块”问题,已经在SO上有一些好的答案。我喜欢Brian的方法here,根据你的问题,它会是:
[ let linesToJoin = ResizeArray()
for line in rawLines do
if line.StartsWith("Header:") && linesToJoin.Count > 0 then
yield String.Join("", linesToJoin)
linesToJoin.Clear()
linesToJoin.Add(line)
if linesToJoin.Count > 0 then
yield String.Join("", linesToJoin) ]
它不是更优雅,但我认为意图更清晰。
另一种选择是使用Tomas' groupWhen
function(see usage)。
答案 1 :(得分:2)
你可以做一个非常相似的尾递归函数,并避免这两个步骤:
let rec combineLines (currentLine:string) combinedLines = function
| (line:string)::tail when line.StartsWith "Header:" && currentLine <> "" ->
combineLines line (currentLine::combinedLines) tail
| line::tail -> combineLines (currentLine + line) combinedLines tail
| [] -> currentLine::combinedLines
lines |> combineLines "" [] |> List.rev
答案 2 :(得分:2)
如果您使用foldBack
从最后处理,这实际上更简单,特别是您不需要反转结果,因此它应该更快:
let processedLines =
(rawLines, ("", []))
||> List.foldBack (fun line (currentLine, allLines) ->
if line.StartsWith "Header:" then
"", line + currentLine :: allLines
else
line + currentLine, allLines)
|> function
| "", lines -> lines
| _ -> failwith "The original string didn't start with 'Header:'"