2015-09-26 2 views
7

Мне нужно разобрать входные потоки из сокета. Данные отправляются от клиента Telnet, и поэтому я хочу обработать входящие строки, найдя в потоке первый символ '\r', затем выберем байты перед возвратным символом и, наконец, обработаем любые цифры '\b'.Анализ входящего потока TCP из символов Ascii, handle backspace 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 

Вы заметите, что я назвал значение аккумулятора для 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" 

После того, как один понимает, как моделировать то, как рекурсивную функцию, она должна быть возможность реализовать его с помощью складку вместо, а Райан 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

Чувства, подобные этому, могут быть уменьшены? Не пропускает символ к аккумулятору, если он не является backspace, если он тогда просто устанавливает аккумулятор в хвост?

+0

Должно быть сгибом, а не уменьшением, как указано в ответе Марка Семанна. –

Смежные вопросы