2015-09-26 105 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的答案中所提到的。 –