F# match the beginning of an array

403 views Asked by At

I have a Byte[] buffer that may contain one or multiple data frames, I need to read the first bytes to know how long the actual frame is.

This is a "non-working" version of what I want to do:

let extractFrame (buffer:byte[]) =
  match buffer with 
    | [|head1;head2;head3;..|] when head2 < (byte)128 -> processDataFrame buffer head2
    | <...others....>
    | _ -> raise(new System.Exception())

Basically, I need to evaluate the first three bytes, and then call processDataFrame with the buffer and the actual length of the frame. Depending on the headers, the frame can be data, control, etc...

Can this be done with any kind of match (lists, sequences, ...etc...)? Or will I have to create another small array with just the length of the header?(I would like to avoid this).

2

There are 2 answers

1
Petr On BEST ANSWER

If you want to use matching you could create active pattern (http://msdn.microsoft.com/en-us/library/dd233248.aspx):

let (|Head1|_|) (buffer:byte[]) =
    if(buffer.[0] (* add condition here *)) then Some buffer.[0]
    else None 

let (|Head2|_|) (buffer:byte[]) =
    if(buffer.[1] < (byte)128) then Some buffer.[1]
    else None 

let extractFrame (buffer:byte[]) =
  match buffer with 
    | Head1 h1 -> processDataFrame buffer h1
    | Head2 h2 -> processDataFrame buffer h2
........
    | _ -> raise(new System.Exception())
3
Tomas Petricek On

I think that this might actually be easier to do using the plain if construct.

But as Petr mentioned, you can use active patterns and define your own patterns that extract specific information from the array. To model what you're doing, I would actually use a parameterized active pattern - you can give it the number of elements from the array that you need and it gives you an array with e.g. 3 elements back:

let (|TakeSlice|_|) count (array:_[]) = 
  if array.Length < count then None
  else Some(array.[0 .. count-1])

let extractFrame (buffer:byte[]) =
  match buffer with 
  | TakeSlice 3 [|head1;head2;head3|] when head2 < (byte)128 -> 
      processDataFrame buffer head2
  | <...others....>
  | _ -> raise(new System.Exception())  

One disadvantage of this approach is that your pattern [|h1; h2; h3|] has to match to the length that you specified 3 - the compiler cannot check this for you.