我认为以下功能可以满足您的需求。输入流由垃圾和帧组成。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
从流中获取两个帧:
[<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
这似乎可行,并且应该易于扩展/修改。但对我来说,它看起来像是相当多的代码。欢迎改进。