2015-09-26 103 views
7

我需要从套接字解析输入流。 数据是从Telnet客户端发送的,因此我想通过查找流中的第一个'\r'字符来处理传入的字符串,然后在返回字符前选择字节,最后处理任何字符。解析Ascii字符的传入TCP流,处理退格符char

在这里处理'\b'位的惯用方法是什么? 我目前正在使用一个可变堆栈并将字符推送到它上面,如果有退格,我弹出最后一个字符。 然后只需将结果转换为字符串。

但我觉得可能有一些很好的方式来做到这一点与模式匹配和尾递归。那么,这怎么可以做到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 

回答

11

首先让我们来有问题的部分隔离的功能:

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 

你会发现,我已经叫累加值为accimp函数的类型为char list -> char list -> char list,它与传入char list匹配:如果它为空,则返回累加器;如果它具有'\b'作为头部,则通过使用List.tail从累加器中删除先前的char;在所有其他情况下,它会将第一个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" 

一旦一个懂得如何东西建模为一个递归函数,它应该是可以使用倍来实现它,而不是作为瑞安W¯¯高夫指出。这里有一种方法:

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 
+0

啊是的,它应该折叠不减少,没有意识到在f# –

+3

@RyanWGough'reduce'类似于'fold',但它输入为空时崩溃。 –

+0

谢谢,真棒,这正是我正在寻找的。 –

1

感觉像这样可以用减少?如果不是退格,将字符赋予累加器,如果它只是将累加器设置为尾部?

+0

应该是一个折叠,而不是减少,正如Mark Seemann的答案中所提到的。 –