2015-09-26 5 views
7

ソケットから入力ストリームを解析する必要があります。 データはTelnetクライアントから送信されます。したがって、ストリーム内の最初の'\r'文字を見つけて、戻り文字の前のバイトを選択し、最後にbackspace'\b'文字を処理することによって、受信した文字列を処理します。Asciiの入力TCPストリームの解析、バックスペース文字の処理

ここで'\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 

あなたは、私がaccためのアキュムレータ値と呼ばれてきたことに気づくでしょう。 imp関数のタイプは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" 

1は、再帰関数として何かをモデル化する方法を理解したらライアンWゴフが指摘するように、代わりに折り目を使用して、それを実現することが可能でなければなりません。これを行う方法が1つあります:

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の回答に記載されているように、折り畳みで、縮めないでください。 –

関連する問題