我需要从套接字解析输入流.数据是从Telnet客户端发送的,因此我想通过查找'\r'流中的第一个字符来处理传入的字符串,然后在返回char之前选择字节并最终处理任何backspace '\b'字符.
'\b'在这里处理比特的惯用方法是什么?我目前正在使用一个可变堆栈并将chars推到它上面,如果有一个退格,我会弹出最后一个char.然后将结果转换为字符串.
但我认为使用模式匹配和尾递归可能有一些很好的方法.那么,如何以F#方式完成呢?
let receiveInput (inputBuffer:StringBuilder) (received:Tcp.Received)=
let text = Encoding.ASCII.GetString(received.Data.ToArray());
inputBuffer.Append(text) |> ignore
let all = inputBuffer.ToString()
match all.IndexOf('\r') with
| enter when enter >= 0 ->
let textToProcess = all.Substring(0,enter)
inputBuffer.Remove(0,enter+2) |> ignore
//this is the part I'm wondering about
let stack = new Stack<char>()
for c in textToProcess do
if c = '\b' then stack.Pop() |> ignore
else stack.Push c
let input = new System.String(stack |> Seq.rev |> Seq.toArray)
Some(input)
| _ ->
None
Run Code Online (Sandbox Code Playgroud)
Mar*_*ann 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
Run Code Online (Sandbox Code Playgroud)
这有一个可变变量(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
Run Code Online (Sandbox Code Playgroud)
你会注意到我已经调用了累加器值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"
Run Code Online (Sandbox Code Playgroud)
一旦人们理解了如何将某事物建模为递归函数,就应该可以使用折叠来实现它,正如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
Run Code Online (Sandbox Code Playgroud)