Разбор входящего потока TCP символов Ascii, обработка символа backspace
мне нужно проанализировать входные потоки из сокета.
Данные отправляются из клиента Telnet, и поэтому я хочу обработать входящие строки, найдя первый 'r'
символ в потоке, затем выберите байты перед возвращаемым символом и, наконец, обработайте любой backspace
'b'
chars.
каким был бы идиоматический способ борьбы с 'b'
биты здесь?
В настоящее время я использую изменяемый стек и нажимаю на него символы, и если есть backspace, я открываю последний символ.
Тогда просто повернуть результат в строку.
но я полагаю, что, вероятно, есть хороший способ сделать это с сопоставлением шаблонов и рекурсией хвоста. Итак, как это можно сделать способом 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
2 ответов
давайте начнем с выделения проблемной части функции:
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
. The 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"
как только вы поймете, как моделировать что-то как рекурсивную функцию, должно быть возможно реализовать ее с помощью сгиба, как указывает Райан у Гоф. Есть только один способ. это:
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
чувствует, что это можно сделать с уменьшением? Минусы персонажа к аккумулятору, если это не backspace, если он тогда просто установил аккумулятор на свой хвост?