我需要解析来自套接字的输入流。
数据是从Telnet客户端发送的,因此我想通过查找流中的第一个'\r'
字符来处理传入的字符串,然后在返回字符之前选择字节,最后处理任何backspace
{{ 1}}字符。
在这里处理'\b'
位的惯用方法是什么?
我目前正在使用一个可变堆栈并将chars推到它上面,如果有一个退格,我会弹出最后一个char。
然后将结果转换为字符串。
但我认为使用模式匹配和尾递归可能有一些很好的方法。 那么,怎样才能以F#方式完成呢?
'\b'
答案 0 :(得分:12)
让我们首先将有问题的部分隔离到一个函数:
open System
open System.Collections.Generic
let handleBackspaces textToProcess : string =
let stack = Stack<char>()
for c in textToProcess do
if c = '\b' then stack.Pop() |> ignore
else stack.Push c
stack |> Seq.rev |> Seq.toArray |> String
这有一个可变变量(stack
)。只要有变量变量,就可以在递归函数中用累加器值替换它。这是一种方法:
open System
let handleBackspaces' textToProcess : string =
let rec imp acc = function
| [] -> acc
| '\b'::cs -> imp (acc |> List.tail) cs
| c::cs -> imp (c::acc) cs
textToProcess |> Seq.toList |> imp [] |> List.rev |> List.toArray |> String
你会注意到我调用了acc
的累加器值。 imp
函数的类型为char list -> char list -> char list
,它与传入的char list
匹配:如果它为空,则返回累加器;如果它有'\b'
作为头部,它会使用char
从累加器中删除之前的List.tail
;在所有其他情况下,它是第一个char
到累加器并递归调用自身。
这是一个(希望令人满意的)FSI会议:
> handleBackspaces' "b\bfoo";;
val it : string = "foo"
> handleBackspaces' "foo";;
val it : string = "foo"
> handleBackspaces' "bar\bz";;
val it : string = "baz"
> handleBackspaces' "bar\b\boo";;
val it : string = "boo"
> handleBackspaces' "b\bfa\boo";;
val it : string = "foo"
一旦人们理解了如何将某事物建模为递归函数,就应该可以使用折叠来实现它,正如Ryan W Gough指出的那样。这是一种方法:
let handleBackspaces'' textToProcess : string =
textToProcess
|> Seq.fold (fun acc c -> if c = '\b' then acc |> List.tail else c::acc) []
|> List.rev
|> List.toArray
|> String
答案 1 :(得分:1)
这样的感觉可以通过减少来完成吗?如果它不是退格,那么将字符构造给累加器,如果它只是将累加器设置为它的尾部?