I've got a discriminated union tree like this:
type rbtree =
| LeafB of int
| LeafR of int
| Node of int*rbtree*rbtree
And what I have to do is to search for every LeafB present in the tree, so I came with a this recursive function:
let rec searchB (tree:rbtree) : rbtree list =
match tree with
| LeafB(n) -> LeafB(n)::searchB tree
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
But when I try to test it I get stack overflow exception and I have no idea how to modify it to work properly.
As #kvb says your updated version isn't truely tail-rec and might cause a stackoverflow as well.
What you can do is using continuations essentially using heap space instead of stack space.
let searchB_ tree =
let rec tail results continuation tree =
match tree with
| LeafB v -> continuation (v::results)
| LeafR _ -> continuation results
| Node (_, lt, rt) -> tail results (fun leftResults -> tail leftResults continuation rt) lt
tail [] id tree |> List.rev
If we looks at the generated code in ILSpy it looks essentially like this:
internal static a tail#13<a>(FSharpList<int> results, FSharpFunc<FSharpList<int>, a> continuation, Program.rbtree tree)
{
while (true)
{
Program.rbtree rbtree = tree;
if (rbtree is Program.rbtree.LeafR)
{
goto IL_34;
}
if (!(rbtree is Program.rbtree.Node))
{
break;
}
Program.rbtree.Node node = (Program.rbtree.Node)tree;
Program.rbtree rt = node.item3;
FSharpList<int> arg_5E_0 = results;
FSharpFunc<FSharpList<int>, a> arg_5C_0 = new Program<a>.tail#17-1(continuation, rt);
tree = node.item2;
continuation = arg_5C_0;
results = arg_5E_0;
}
Program.rbtree.LeafB leafB = (Program.rbtree.LeafB)tree;
int v = leafB.item;
return continuation.Invoke(FSharpList<int>.Cons(v, results));
IL_34:
return continuation.Invoke(results);
}
So as expected with tail recursive functions in F# it is tranformed into a while loop. If we look at the non-tail recursive function:
// Program
public static FSharpList<int> searchB(Program.rbtree tree)
{
if (tree is Program.rbtree.LeafR)
{
return FSharpList<int>.Empty;
}
if (!(tree is Program.rbtree.Node))
{
Program.rbtree.LeafB leafB = (Program.rbtree.LeafB)tree;
return FSharpList<int>.Cons(leafB.item, FSharpList<int>.Empty);
}
Program.rbtree.Node node = (Program.rbtree.Node)tree;
Program.rbtree right = node.item3;
Program.rbtree left = node.item2;
return Operators.op_Append<int>(Program.searchB(left), Program.searchB(right));
}
We see the recursive call at the end of the function Operators.op_Append<int>(Program.searchB(left), Program.searchB(right));
So the tail-recursive function allocates continuations functions instead of creating a new stack frame. We can still run out of heap but there's lot more heap than stack.
Full example demonstrating a stackoverflow:
type rbtree =
| LeafB of int
| LeafR of int
| Node of int*rbtree*rbtree
let rec searchB tree =
match tree with
| LeafB(n) -> n::[]
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
let searchB_ tree =
let rec tail results continuation tree =
match tree with
| LeafB v -> continuation (v::results)
| LeafR _ -> continuation results
| Node (_, lt, rt) -> tail results (fun leftResults -> tail leftResults continuation rt) lt
tail [] id tree |> List.rev
let rec genTree n =
let rec loop i t =
if i > 0 then
loop (i - 1) (Node (i, t, LeafB i))
else
t
loop n (LeafB n)
[<EntryPoint>]
let main argv =
printfn "generate left leaning tree..."
let tree = genTree 100000
printfn "tail rec"
let s = searchB_ tree
printfn "rec"
let f = searchB tree
printfn "Is equal? %A" (f = s)
0
Oh, I might came with an solution:
let rec searchB (tree:rbtree) : rbtree list =
match tree with
| LeafB(n) -> LeafB(n)::[]
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
Now it looks like working properly when I try it.
Related
I have a sequence of value that I would like to apply to a function partially :
let f a b c d e= a+b+c+d+e
let items = [1,2,3,4,5]
let result = applyPartially f items
Assert.Equal(15, result)
I am looking after the applyPartially function. I have tried writing recursive functions like this :
let rec applyPartially f items =
| [] -> f
| [x] -> f x
| head :: tail -> applyPartially (f head) tail
The problem I have encountered is that the f type is at the beginning of my iteration 'a->'b->'c->'d->'e, and for every loop it should consume an order.
'a->'b->'c->'d->'e
'b->'c->'d->'e
'c->'d->'e
'd->'e
That means that the lower interface I can think of would be 'd->'e. How could I hide the complexity of my function so that only 'd->'e is shown in the recursive function?
The F# type system does not have a nice way of working with ordinary functions in a way you are suggesting - to do this, you'd need to make sure that the length of the list matches the number of arguments of the function, which is not possible with ordinary lists and functions.
However, you can model this nicely using a discriminated union. You can define a partial function, which has either completed, or needs one more input:
type PartialFunction<'T, 'R> =
| Completed of 'R
| NeedsMore of ('T -> PartialFunction<'T, 'R>)
Your function f can now be written (with a slightly ugly syntax) as a PartialFunction<int, int> that keeps taking 5 inputs and then returns the result:
let f =
NeedsMore(fun a -> NeedsMore(fun b ->
NeedsMore(fun c -> NeedsMore(fun d ->
NeedsMore(fun e -> Completed(a+b+c+d+e))))))
Now you can implement applyPartially by deconstructing the list of arguments and applying them one by one to the partial function until you get the result:
let rec applyPartially f items =
match f, items with
| Completed r, _ -> r
| NeedsMore f, head::tail -> applyPartially (f head) tail
| NeedsMore _, _ -> failwith "Insufficient number of arguments"
The following now returns 15 as expected:
applyPartially f [1;2;3;4;5]
Disclaimer: Please don't use this. This is just plain evil.
let apply f v =
let args = v |> Seq.toArray
f.GetType().GetMethods()
|> Array.tryFind (fun m -> m.Name = "Invoke" && Array.length (m.GetParameters()) = Array.length args)
|> function None -> failwith "Not enough args" | Some(m) -> m.Invoke(f, args)
Just like you would expect:
let f a b c d e= a+b+c+d+e
apply f [1; 2; 3; 4; 5] //15
Is it possible to implement a tail recursive version of the quick sort algorithm (via the continuation pattern)? And if it is, how would one implement it?
Normal (not optimized) version:
let rec quicksort list =
match list with
| [] -> []
| element::[] -> [element]
| pivot::rest -> let ``elements smaller than pivot``, ``elements larger or equal to pivot``=
rest |> List.partition(fun element -> element < pivot)
quicksort ``elements smaller than pivot`` # [pivot] # quicksort ``elements larger or equal to pivot``
Direct style:
let rec quicksort list =
match list with
| [] -> []
| [element] -> [element]
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
let sorted_left = quicksort left in
let sorted_right = quicksort right in
sorted_left # [pivot] # sorted_right
My first, naive translation is very similar to Laurent's version, except indented a bit weirdly to make apparent that calls with continuations are really a kind of binding:
let rec quicksort list cont =
match list with
| [] -> cont []
| element::[] -> cont [element]
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
quicksort left (fun sorted_left ->
quicksort right (fun sorted_right ->
cont (sorted_left # [pivot] # sorted_right)))
let qsort li = quicksort li (fun x -> x)
Contrarily to Laurent, I find it easy to check that cont is not forgotten: CPS functions translated from direct style have the property that the continuation is used linearily, once and only once in each branch, in tail position. It is easy to check that no such call was forgotten.
But in fact, for most runs of quicksort (supposing you get a roughly logarithmic behavior because you're not unlucky or you shuffled the input first), the call stack is not an issue, as it only grows logarithmically. Much more worrying are the frequent calls to # wich is linear in its left parameter. A common optimization technique is to define functions not as returning a list but as "adding input to an accumulator list":
let rec quicksort list accu =
match list with
| [] -> accu
| element::[] -> element::accu
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
let sorted_right = quicksort right accu in
quicksort left (pivot :: sorted_right)
let qsort li = quicksort li []
Of course this can be turned into CPS again:
let rec quicksort list accu cont =
match list with
| [] -> cont accu
| element::[] -> cont (element::accu)
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
quicksort right accu (fun sorted_right ->
quicksort left (pivot :: sorted_right) cont)
let qsort li = quicksort li [] (fun x -> x)
Now a last trick is to "defunctionalize" the continuations by turning them into data structure (supposing the allocation of data structures is slightly more efficient than the allocation of a closure):
type 'a cont =
| Left of 'a list * 'a * 'a cont
| Return
let rec quicksort list accu cont =
match list with
| [] -> eval_cont cont accu
| element::[] -> eval_cont cont (element::accu)
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
quicksort right accu (Left (left, pivot, cont))
and eval_cont = function
| Left (left, pivot, cont) ->
(fun sorted_right -> quicksort left (pivot :: sorted_right) cont)
| Return -> (fun x -> x)
let qsort li = quicksort li [] Return
Finally, I chose the function .. fun style for eval_cont to make it apparent that those were just pieces of code from the CPS version, but the following version is probably better optimized by arity-raising:
and eval_cont cont accu = match cont with
| Left (left, pivot, cont) ->
quicksort left (pivot :: accu) cont
| Return -> accu
Quick attempt, seeems to work:
let rec quicksort list cont =
match list with
| [] -> cont []
| element::[] -> cont [element]
| pivot::rest ->
let ``elements smaller than pivot``, ``elements larger or equal to pivot`` =
rest |> List.partition (fun element -> element < pivot)
quicksort ``elements smaller than pivot``
(fun x -> quicksort ``elements larger or equal to pivot`` (fun y -> cont (x # [pivot] # y)))
> quicksort [2; 6; 3; 8; 5; 1; 9; 4] id;;
val it : int list = [1; 2; 3; 4; 5; 6; 8; 9]
Edit:
Of course, this code is highly inefficient. I hope nobody will use it in real code.
The code was not difficult to write, but continuations might be difficult to read and can be error-prone (it's easy to forget a call to cont). If you want to play more, you can write a continuation monad (Brian wrote a blog post about it).
Continuation monad (stolen from here) can also be used (usually makes code more readable):
type ContinuationMonad() =
// ma -> (a -> mb) -> mb
member this.Bind (m, f) = fun c -> m (fun a -> f a c)
// a -> ma
member this.Return x = fun k -> k x
// ma -> ma
member this.ReturnFrom m = m
let cont = ContinuationMonad()
// Monadic definition of QuickSort
// it's shame F# doesn't allow us to use generic monad code
// (we need to use 'cont' monad here)
// otherwise we could run the same code as Identity monad, for instance
// producing direct (non-cont) behavior
let rec qsm = function
|[] -> cont.Return []
|x::xs -> cont {
let l,r = List.partition ((>=)x) xs
let! ls = qsm l
let! rs = qsm r
return (ls # x :: rs) }
// Here we run our cont with id
let qs xs = qsm xs id
printf "%A" (qs [2;6;3;8;5;1;9;4])
This is hurting my brain!
I want to recurse over a tree structure and collect all instances that match some filter into one list.
Here's a sample tree structure
type Tree =
| Node of int * Tree list
Here's a test sample tree:
let test =
Node((1,
[Node(2,
[Node(3,[]);
Node(3,[])]);
Node(3,[])]))
Collecting and filtering over nodes with and int value of 3 should give you output like this:
[Node(3,[]);Node(3,[]);Node(3,[])]
The following recursive function should do the trick:
// The 'f' parameter is a predicate specifying
// whether element should be included in the list or not
let rec collect f (Node(n, children) as node) =
// Process recursively all children
let rest = children |> List.collect (collect f)
// Add the current element to the front (in case we want to)
if (f n) then node::rest else rest
// Sample usage
let nodes = collect (fun n -> n%3 = 0) tree
The function List.collect applies the provided function to all elements of the
list children - each call returns a list of elements and List.collect
concatenates all the returned lists into a single one.
Alternatively you could write (this maay help understanding how the code works):
let rest =
children |> List.map (fun n -> collect f n)
|> List.concat
The same thing can be also written using list comprehensions:
let rec collect f (Node(n, children) as node) =
[ for m in children do
// add all returned elements to the result
yield! collect f m
// add the current node if the predicate returns true
if (f n) then yield node ]
EDIT: Updated code to return nodes as pointed out by kvb.
BTW: It is generally a good idea to show some code that you tried to write so far. This helps people understand what part you didn't understand and so you'll get more helpful answers (and it is also considered as polite)
A more complex tail recursive solution.
let filterTree (t : Tree) (predicate : int -> bool) =
let rec filter acc = function
| (Node(i, []) as n)::tail ->
if predicate i then filter (n::acc) tail
else filter acc tail
| (Node(i, child) as n)::tail ->
if predicate i then filter (n::acc) (tail # child)
else filter acc (tail # child)
| [] -> acc
filter [] [t]
Just for showing usage of F# Sequences Expression (maybe not the best approach, Tomas's solution more likely better I think):
type Tree =
| Node of int * Tree list
let rec filterTree (t : Tree) (predicate : int -> bool) =
seq {
match t with
| Node(i, tl) ->
if predicate i then yield t
for tree in tl do
yield! filterTree tree predicate
}
let test = Node (1, [ Node(2, [ Node(3,[]); Node(3,[]) ]); Node(3,[]) ])
printfn "%A" (filterTree test (fun x -> x = 3))
When my brain hurts cuz it's stuck up a tree, I try to say what I want as simply and clearly as I can:
Given a tree of info, list all sub-trees matching a predicate (in this case, info = 3).
One straightforward way to do it is to list all nodes of the tree, then filter on the predicate.
type 'info tree = Node of 'info * 'info tree list
let rec visit = function
| Node( info, [] ) as node -> [ node ]
| Node( info, children ) as node -> node :: List.collect visit children
let filter predicate tree =
visit tree
|> List.filter (fun (Node(info,_)) -> predicate info)
Here's the tree filter run against the OP's sample data:
let result = filter (fun info -> info = 3) test
One thing that surprised me is how easily the code works for any 'info type with the appropriate predicate:
let test2 =
Node(("One",
[Node("Two",
[Node("Three",[Node("Five",[]);Node("Three",[])]);
Node("Three",[])]);
Node("Three",[])]))
let res2 = filter (fun info -> info = "Three") test2
Alternatively, if you wanted to list the info rather than the sub-trees, the code is breath-takingly simple:
let rec visit = function
| Node( info, [] ) -> [ info ]
| Node( info, children ) -> info :: List.collect visit children
let filter predicate tree =
visit tree
|> List.filter predicate
which supports the same queries but only returns the 'info records, not the tree structure:
let result = filter (fun info -> info = 3) test
> val result : int list = [3; 3; 3; 3]
Tomas's approach looks standard, but doesn't quite match your example. If you actually want the list of matching nodes rather than values, this should work:
let rec filter f (Node(i,l) as t) =
let rest = List.collect (filter f) l
if f t then t::rest
else rest
let filtered = filter (fun (Node(i,_)) -> i=3) test
Here is an over engineered solution but it shows seperation of concerns with Partial Active Patterns. This isn't the best example for partial active patterns but it was a fun exercise nonetheless. Match statements are evaluated in order.
let (|EqualThree|_|) = function
| Node(i, _) as n when i = 3 -> Some n
| _ -> None
let (|HasChildren|_|) = function
| Node(_, []) -> None
| Node(_, children) as n -> Some children
| _ -> None
let filterTree3 (t : Tree) (predicate : int -> bool) =
let rec filter acc = function
| EqualThree(n)::tail & HasChildren(c)::_ -> filter (n::acc) (tail # c)
| EqualThree(n)::tail -> filter (n::acc) tail
| HasChildren(c)::tail -> filter acc (tail # c)
| _::tail -> filter acc tail
| [] -> acc
filter [] [t]
I have two snippets of code that tries to convert a float list to a Vector3 or Vector2 list. The idea is to take 2/3 elements at a time from the list and combine them as a vector. The end result is a sequence of vectors.
let rec vec3Seq floatList =
seq {
match floatList with
| x::y::z::tail -> yield Vector3(x,y,z)
yield! vec3Seq tail
| [] -> ()
| _ -> failwith "float array not multiple of 3?"
}
let rec vec2Seq floatList =
seq {
match floatList with
| x::y::tail -> yield Vector2(x,y)
yield! vec2Seq tail
| [] -> ()
| _ -> failwith "float array not multiple of 2?"
}
The code looks very similiar and yet there seems to be no way to extract a common portion. Any ideas?
Here's one approach. I'm not sure how much simpler this really is, but it does abstract some of the repeated logic out.
let rec mkSeq (|P|_|) x =
seq {
match x with
| P(p,tail) ->
yield p
yield! mkSeq (|P|_|) tail
| [] -> ()
| _ -> failwith "List length mismatch" }
let vec3Seq =
mkSeq (function
| x::y::z::tail -> Some(Vector3(x,y,z), tail)
| _ -> None)
As Rex commented, if you want this only for two cases, then you probably won't have any problem if you leave the code as it is. However, if you want to extract a common pattern, then you can write a function that splits a list into sub-list of a specified length (2 or 3 or any other number). Once you do that, you'll only use map to turn each list of the specified length into Vector.
The function for splitting list isn't available in the F# library (as far as I can tell), so you'll have to implement it yourself. It can be done roughly like this:
let divideList n list =
// 'acc' - accumulates the resulting sub-lists (reversed order)
// 'tmp' - stores values of the current sub-list (reversed order)
// 'c' - the length of 'tmp' so far
// 'list' - the remaining elements to process
let rec divideListAux acc tmp c list =
match list with
| x::xs when c = n - 1 ->
// we're adding last element to 'tmp',
// so we reverse it and add it to accumulator
divideListAux ((List.rev (x::tmp))::acc) [] 0 xs
| x::xs ->
// add one more value to 'tmp'
divideListAux acc (x::tmp) (c+1) xs
| [] when c = 0 -> List.rev acc // no more elements and empty 'tmp'
| _ -> failwithf "not multiple of %d" n // non-empty 'tmp'
divideListAux [] [] 0 list
Now, you can use this function to implement your two conversions like this:
seq { for [x; y] in floatList |> divideList 2 -> Vector2(x,y) }
seq { for [x; y; z] in floatList |> divideList 3 -> Vector3(x,y,z) }
This will give a warning, because we're using an incomplete pattern that expects that the returned lists will be of length 2 or 3 respectively, but that's correct expectation, so the code will work fine. I'm also using a brief version of sequence expression the -> does the same thing as do yield, but it can be used only in simple cases like this one.
This is simular to kvb's solution but doesn't use a partial active pattern.
let rec listToSeq convert (list:list<_>) =
seq {
if not(List.isEmpty list) then
let list, vec = convert list
yield vec
yield! listToSeq convert list
}
let vec2Seq = listToSeq (function
| x::y::tail -> tail, Vector2(x,y)
| _ -> failwith "float array not multiple of 2?")
let vec3Seq = listToSeq (function
| x::y::z::tail -> tail, Vector3(x,y,z)
| _ -> failwith "float array not multiple of 3?")
Honestly, what you have is pretty much as good as it can get, although you might be able to make a little more compact using this:
// take 3 [1 .. 5] returns ([1; 2; 3], [4; 5])
let rec take count l =
match count, l with
| 0, xs -> [], xs
| n, x::xs -> let res, xs' = take (count - 1) xs in x::res, xs'
| n, [] -> failwith "Index out of range"
// split 3 [1 .. 6] returns [[1;2;3]; [4;5;6]]
let rec split count l =
seq { match take count l with
| xs, ys -> yield xs; if ys <> [] then yield! split count ys }
let vec3Seq l = split 3 l |> Seq.map (fun [x;y;z] -> Vector3(x, y, z))
let vec2Seq l = split 2 l |> Seq.map (fun [x;y] -> Vector2(x, y))
Now the process of breaking up your lists is moved into its own generic "take" and "split" functions, its much easier to map it to your desired type.
(I am aware of this question, but it relates to sequences, which is not my problem here)
Given this input (for example):
let testlist =
[
"*text1";
"*text2";
"text3";
"text4";
"*text5";
"*text6";
"*text7"
]
let pred (s:string) = s.StartsWith("*")
I would like to be able to call MyFunc pred testlist and get this output:
[
["*text1";"*text2"];
["*text5";"*text6";"*text7"]
]
This is my current solution, but I don't really like the nested List.revs (ignore the fact that it takes Seq as input)
let shunt pred sq =
let shunter (prevpick, acc) (pick, a) =
match pick, prevpick with
| (true, true) -> (true, (a :: (List.hd acc)) :: (List.tl acc))
| (false, _) -> (false, acc)
| (true, _) -> (true, [a] :: acc)
sq
|> Seq.map (fun a -> (pred a, a))
|> Seq.fold shunter (false, [])
|> snd
|> List.map List.rev
|> List.rev
there is a List.partition function in the F# core library (in case you wanted to implement this just to have it working and not to learn how to write recursive functions yourself). Using this function, you can write this:
> testlist |> List.partition (fun s -> s.StartsWith("*"))
val it : string list * string list =
(["*text1"; "*text2"; "*text5"; "*text6"; "*text7"], ["text3"; "text4"])
Note that this function returns a tuple instead of returning a list of lists. This is a bit different to what you wanted, but if the predicate returns just true or false, then this makes more sense.
The implementation of partition function that returns tuples is also a bit simpler, so it may be useful for learning purposes:
let partition pred list =
// Helper function, which keeps results collected so
// far in 'accumulator' arguments outTrue and outFalse
let rec partitionAux list outTrue outFalse =
match list with
| [] ->
// We need to reverse the results (as we collected
// them in the opposite order!)
List.rev outTrue, List.rev outFalse
// Append element to one of the lists, depending on 'pred'
| x::xs when pred x -> partitionAux xs (x::outTrue) outFalse
| x::xs -> partitionAux xs outTrue (x::outFalse)
// Run the helper function
partitionAux list [] []
Edit: rev-less version using foldBack added below.
Here's some code that uses lists and tail-recursion:
//divides a list L into chunks for which all elements match pred
let divide pred L =
let rec aux buf acc L =
match L,buf with
//no more input and an empty buffer -> return acc
| [],[] -> List.rev acc
//no more input and a non-empty buffer -> return acc + rest of buffer
| [],buf -> List.rev (List.rev buf :: acc)
//found something that matches pred: put it in the buffer and go to next in list
| h::t,buf when pred h -> aux (h::buf) acc t
//found something that doesn't match pred. Continue but don't add an empty buffer to acc
| h::t,[] -> aux [] acc t
//found input that doesn't match pred. Add buffer to acc and continue with an empty buffer
| h::t,buf -> aux [] (List.rev buf :: acc) t
aux [] [] L
usage:
> divide pred testlist;;
val it : string list list =
[["*text1"; "*text2"]; ["*text5"; "*text6"; "*text7"]]
Using a list as data structure for a buffer means that it always needs to be reversed when outputting the contents. This may not be a problem if individual chunks are modestly sized. If speed/efficiency becomes an issue, you could use a Queue<'a> or a `List<'a>' for the buffers, for which appending is fast. But using these data structures instead of lists also means that you lose the powerful list pattern matching. In my opinion, being able to pattern match lists outweighs the presence of a few List.rev calls.
Here's a streaming version that outputs the result one block at a time. This avoids the List.rev on the accumulator in the previous example:
let dividestream pred L =
let rec aux buf L =
seq { match L, buf with
| [],[] -> ()
| [],buf -> yield List.rev buf
| h::t,buf when pred h -> yield! aux (h::buf) t
| h::t,[] -> yield! aux [] t
| h::t,buf -> yield List.rev buf
yield! aux [] t }
aux [] L
This streaming version avoids the List.rev on the accumulator. Using List.foldBack can be used to avoid reversing the accumulated chunks as well.
update: here's a version using foldBack
//divides a list L into chunks for which all elements match pred
let divide2 pred L =
let f x (acc,buf) =
match pred x,buf with
| true,buf -> (acc,x::buf)
| false,[] -> (acc,[])
| false,buf -> (buf::acc,[])
let rest,remainingBuffer = List.foldBack f L ([],[])
match remainingBuffer with
| [] -> rest
| buf -> buf :: rest
Just reverse the list once up front, and then build the structure in order easily:
let Shunt p l =
let mutable r = List.rev l
let mutable result = []
while not r.IsEmpty do
let mutable thisBatch = []
while not r.IsEmpty && not(p r.Head) do
r <- r.Tail
while not r.IsEmpty && p r.Head do
thisBatch <- r.Head :: thisBatch
r <- r.Tail
if not thisBatch.IsEmpty then
result <- thisBatch :: result
result
The outer while deals with each 'batch', and the first inner while skips over any that don't match the predicate, followed by another while that grabs all those that do and stores them in the current batch. If there was anything in this batch (the final one may be empty), prepend it to the final result.
This is an example where I think locally imperative code is simply superior to a purely functional counterpart. The code above is so easy to write and to reason about.
Another version of shunt:
let shunt pred lst =
let rec tWhile pred lst =
match lst with
| [] -> [], []
| hd :: tl when pred hd -> let taken, rest = tWhile pred tl
(hd :: taken), rest
| lst -> [], lst
let rec collect = function
| [] -> []
| lst -> let taken, rest = tWhile pred lst
taken :: (collect (snd (tWhile (fun x -> not (pred x)) rest)))
collect lst
This one avoids List.rev but it's not tail recursive - so only suitable for small lists.
yet another one...
let partition pred lst =
let rec trec xs cont =
match xs with
| [] -> ([],[]) |> cont
| h::t when pred h -> (fun (y,n) -> h::y,n) >> cont |> trec t
| h::t -> (fun (y,n) -> y,h::n) >> cont |> trec t
trec lst id
then we can define shunt:
let shunt pred lst = lst |> partition pred |> (fun (x,y) -> [x;y])