次の関数はあなたが望むことをすると思います。入力ストリームはガベージとフレームで構成されます。Gargabe は、最初またはフレーム間で発生する可能性があります。ガベージが多すぎるか、フレームが長すぎる場合は、エラーが報告されます。それ以外の場合は、次のフレームが返されます。
let [<Literal>] FrameStart = 0xFE
let [<Literal>] FrameEnd = 0xFF
let [<Literal>] MaxGarbageLength = 5
let [<Literal>] MaxFrameLength = 5
type State<'T> =
| Garbage of int
| Frame of int * 'T list
let getFrame stream =
let getNextRest stream =
match Seq.isEmpty stream with
| true -> None
| false -> Some(Seq.head stream, Seq.skip 1 stream)
let rec parse state stream =
match getNextRest stream with
| None -> None
| Some(next, rest) ->
match state with
| Garbage n when n >= MaxGarbageLength -> None
| Garbage n ->
match next with
| FrameStart -> parse (Frame(0, [])) rest
| _ -> parse (Garbage(n+1)) rest
| Frame(n, _) when n >= MaxFrameLength -> None
| Frame(n, content) ->
match next with
| FrameEnd -> Some(content, rest)
| _ -> parse (Frame(n+1, content @ [next])) rest
parse (Garbage 0) stream
ストリームから 2 つのフレームを取得するには:
[<Test>]
let ``can parse two frames with garbage in between``() =
let stream = Seq.ofList [1;2;3;FrameStart;4;5;6;FrameEnd;7;8;FrameStart;9;0;FrameEnd]
let (frame1, rest) = (getFrame stream).Value
frame1 |> should equal [4;5;6]
rest |> should equal [7;8;FrameStart;9;0;FrameEnd]
let (frame2, rest) = (getFrame rest).Value
frame2 |> should equal [9;0]
rest |> should equal []
エラーは、None を返すことで正しく検出されます (MaxGarbageLength は 5 であるため、以下はエラーを報告することに注意してください)。
[<Test>]
let ``none is returned when there is too much garbage``() =
let stream = [1;2;3;4;5;6;FrameStart;7;8;9;FrameEnd]
(getFrame stream).IsNone |> should equal true
これは機能しているようで、簡単に拡張/変更できるはずです。しかし、私にはかなりのコードのように見えます。改善歓迎。