I need to extract the sequence of equal chars in a text.
For example:
The string "aaaBbbcccccccDaBBBzcc11211" should be converted to a list of strings like
["aaa";"B";"bb";"ccccccc";"D";"a";"BBB";"z";"cc";"11";"2";"11"].
That's my solution until now:
let groupSequences (text:string) =
let toString chars =
System.String(chars |> Array.ofList)
let rec groupSequencesRecursive acc chars = seq {
match (acc, chars) with
| [], c :: rest ->
yield! groupSequencesRecursive [c] rest
| _, c :: rest when acc.[0] <> c ->
yield (toString acc)
yield! groupSequencesRecursive [c] rest
| _, c :: rest when acc.[0] = c ->
yield! groupSequencesRecursive (c :: acc) rest
| _, [] ->
yield (toString acc)
| _ ->
yield ""
}
text
|> List.ofSeq
|> groupSequencesRecursive []
groupSequences "aaaBbbcccccccDaBBBzcc11211"
|> Seq.iter (fun x -> printfn "%s" x)
|> ignore
I'm a F# newbie.
This solution can be better?
Here a completely generic implementation:
let group xs =
let folder x = function
| [] -> [[x]]
| (h::t)::ta when h = x -> (x::h::t)::ta
| acc -> [x]::acc
Seq.foldBack folder xs []
This function has the type seq<'a> -> 'a list list when 'a : equality, so works not only on strings, but on any (finite) sequence of elements, as long as the element type supports equality comparison.
Used with the input string in the OP, the return value isn't quite in the expected shape:
> group "aaaBbbcccccccDaBBBzcc11211";;
val it : char list list =
[['a'; 'a'; 'a']; ['B']; ['b'; 'b']; ['c'; 'c'; 'c'; 'c'; 'c'; 'c'; 'c'];
['D']; ['a']; ['B'; 'B'; 'B']; ['z']; ['c'; 'c']; ['1'; '1']; ['2'];
['1'; '1']]
Instead of a string list, the return value is a char list list. You can easily convert it to a list of strings using a map:
> group "aaaBbbcccccccDaBBBzcc11211" |> List.map (List.toArray >> System.String);;
val it : System.String list =
["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
This takes advantage of the String constructor overload that takes a char[] as input.
As initially stated, this implementation is generic, so can also be used with other types of lists; e.g. integers:
> group [1;1;2;2;2;3;4;4;3;3;3;0];;
val it : int list list = [[1; 1]; [2; 2; 2]; [3]; [4; 4]; [3; 3; 3]; [0]]
How about with groupby
"aaaBbbcccccccD"
|> Seq.groupBy id
|> Seq.map (snd >> Seq.toArray)
|> Seq.map (fun t -> new string (t))
If you input order matters, here is a method that works
"aaaBbbcccccccDaBBBzcc11211"
|> Seq.pairwise
|> Seq.toArray
|> Array.rev
|> Array.fold (fun (accum::tail) (ca,cb) -> if ca=cb then System.String.Concat(accum,string ca)::tail else string(ca)::accum::tail) (""::[])
This one is also based on recursion though the matching gets away with smaller number of checks.
let chop (txt:string) =
let rec chopInner txtArr (word: char[]) (res: List<string>) =
match txtArr with
| h::t when word.[0] = h -> chopInner t (Array.append word [|h|]) res
| h::t when word.[0] <> h ->
let newWord = word |> (fun s -> System.String s)
chopInner t [|h|] (List.append res [newWord])
| [] ->
let newWord = word |> (fun s -> System.String s)
(List.append res [newWord])
let lst = txt.ToCharArray() |> Array.toList
chopInner lst.Tail [|lst.Head|] []
And the result is as expected:
val text : string = "aaaBbbcccccccDaBBBzcc11211"
> chop text;;
val it : string list =
["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
When you're folding, you'll need to carry along both the previous value and the accumulator holding the temporary results. The previous value is wrapped as option to account for the first iteration. Afterwards, the final result is extracted and reversed.
"aaaBbbcccccccDaBBBzcc11211"
|> Seq.map string
|> Seq.fold (fun state ca ->
Some ca,
match state with
| Some cb, x::xs when ca = cb -> x + ca::xs
| _, xss -> ca::xss )
(None, [])
|> snd
|> List.rev
// val it : string list =
// ["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
Just interesting why everyone publishing solutions based on match-with? Why not go plain recursion?
let rec groups i (s:string) =
let rec next j = if j = s.Length || s.[i] <> s.[j] then j else next(j+1)
if i = s.Length then []
else let j = next i in s.Substring(i, j - i) :: (groups j s)
"aaaBbbcccccccDaBBBzcc11211" |> groups 0
val it : string list = ["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
As someone other here:
Know thy fold ;-)
let someString = "aaaBbbcccccccDaBBBzcc11211"
let addLists state elem =
let (p, ls) = state
elem,
match p = elem, ls with
| _, [] -> [ elem.ToString() ]
| true, h :: t -> (elem.ToString() + h) :: t
| false, h :: t -> elem.ToString() :: ls
someString
|> Seq.fold addLists ((char)0, [])
|> snd
|> List.rev
Related
I have the following list of tuples ordered by the first item. I want to cluster the times by
If the second item of the tuple is greater then 50, it will be in its own cluster.
Otherwise, cluster the items whose sum is less than 50.
The order cannot be changed.
code:
let values =
[("ACE", 78);
("AMR", 3);
("Aam", 6);
("Acc", 1);
("Adj", 23);
("Aga", 12);
("All", 2);
("Ame", 4);
("Amo", 60);
//....
]
values |> Seq.groupBy(fun (k,v) -> ???)
The expected value will be
[["ACE"] // 78
["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"] // 47
["Ame"] // 4
["Amo"] // 60
....]
Ideally, I want to evenly distribute the second group (["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"] which got the sum of 47) and the third one (["Ame"] which has only 4).
How to implement it in F#?
I had the following solution. It uses a mutable variable. It's not F# idiomatic? Is for ... do imperative in F# or is it a syntactic sugar of some function construct?
seq {
let mutable c = []
for v in values |> Seq.sortBy(fun (k, _) -> k) do
let sum = c |> Seq.map(fun (_, v) -> v) |> Seq.sum
if not(c = []) && sum + (snd v) > 50
then
yield c
c <- [v]
else
c <- List.append c [v]
}
I think I got it. Not the nicest code ever, but works and is immutable.
let foldFn (acc:(string list * int) list) (name, value) =
let addToLast last =
let withoutLast = acc |> List.filter ((<>) last)
let newLast = [((fst last) # [name]), (snd last) + value]
newLast |> List.append withoutLast
match acc |> List.tryLast with
| None -> [[name],value]
| Some l ->
if (snd l) + value <= 50 then addToLast l
else [[name], value] |> List.append acc
values |> List.fold foldFn [] |> List.map fst
Update: Since append can be quite expensive operation, I added prepend only version (still fulfills original requirement to keep order).
let foldFn (acc:(string list * int) list) (name, value) =
let addToLast last =
let withoutLast = acc |> List.filter ((<>) last) |> List.rev
let newLast = ((fst last) # [name]), (snd last) + value
(newLast :: withoutLast) |> List.rev
match acc |> List.tryLast with
| None -> [[name],value]
| Some l ->
if (snd l) + value <= 50 then addToLast l
else ([name], value) :: (List.rev acc) |> List.rev
Note: There is still # operator on line 4 (when creating new list of names in cluster), but since the theoretical maximum amount of names in cluster is 50 (if all of them would be equal 1), the performance here is negligible.
If you remove List.map fst on last line, you would get sum value for each cluster in list.
Append operations are expensive. A straight-forward fold with prepended intermediate results is cheaper, even if the lists need to be reversed after processing.
["ACE", 78; "AMR", 3; "Aam", 6; "Acc", 1; "Adj", 23; "Aga", 12; "All", 2; "Ame", 4; "Amd", 6; "Amo", 60]
|> List.fold (fun (r, s1, s2) (t1, t2) ->
if t2 > 50 then [t1]::s1::r, [], 0
elif s2 + t2 > 50 then s1::r, [t1], t2
else r, t1::s1, s2 + t2 ) ([], [], 0)
|> fun (r, s1, _) -> s1::r
|> List.filter (not << List.isEmpty)
|> List.map List.rev
|> List.rev
// val it : string list list =
// [["ACE"]; ["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"]; ["Ame"; "Amd"];
// ["Amo"]]
Here is a recursive version - working much the same way as fold-versions:
let groupBySums data =
let rec group cur sum acc lst =
match lst with
| [] -> acc |> List.where (not << List.isEmpty) |> List.rev
| (name, value)::tail when value > 50 -> group [] 0 ([(name, value)]::(cur |> List.rev)::acc) tail
| (name, value)::tail ->
match sum + value with
| x when x > 50 -> group [(name, value)] 0 ((cur |> List.rev)::acc) tail
| _ -> group ((name, value)::cur) (sum + value) acc tail
(data |> List.sortBy (fun (name, _) -> name)) |> group [] 0 []
values |> groupBySums |> List.iter (printfn "%A")
Yet another solution using Seq.mapFold and Seq.groupBy:
let group values =
values
|> Seq.mapFold (fun (group, total) (name, count) ->
let newTotal = count + total
let newGroup = group + if newTotal > 50 then 1 else 0
(newGroup, name), (newGroup, if newGroup = group then newTotal else count)
) (0, 0)
|> fst
|> Seq.groupBy fst
|> Seq.map (snd >> Seq.map snd >> Seq.toList)
Invoke it like this:
[ "ACE", 78
"AMR", 3
"Aam", 6
"Acc", 1
"Adj", 23
"Aga", 12
"All", 2
"Ame", 4
"Amo", 60
]
|> group
|> Seq.iter (printfn "%A")
// ["ACE"]
// ["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"]
// ["Ame"]
// ["Amo"]
I want to create a function with the signature seq<#seq<'a>> ->seq<seq<'a>> that acts like a Zip method taking a sequence of an arbitrary number of input sequences (instead of 2 or 3 as in Zip2 and Zip3) and returning a sequence of sequences instead of tuples as a result.
That is, given the following input:
[[1;2;3];
[4;5;6];
[7;8;9]]
it will return the result:
[[1;4;7];
[2;5;8];
[3;6;9]]
except with sequences instead of lists.
I am very new to F#, but I have created a function that does what I want, but I know it can be improved. It's not tail recursive and it seems like it could be simpler, but I don't know how yet. I also haven't found a good way to get the signature the way I want (accepting, e.g., an int list list as input) without a second function.
I know this could be implemented using enumerators directly, but I'm interested in doing it in a functional manner.
Here's my code:
let private Tail seq = Seq.skip 1 seq
let private HasLengthNoMoreThan n = Seq.skip n >> Seq.isEmpty
let rec ZipN_core = function
| seqs when seqs |> Seq.isEmpty -> Seq.empty
| seqs when seqs |> Seq.exists Seq.isEmpty -> Seq.empty
| seqs ->
let head = seqs |> Seq.map Seq.head
let tail = seqs |> Seq.map Tail |> ZipN_core
Seq.append (Seq.singleton head) tail
// Required to change the signature of the parameter from seq<seq<'a> to seq<#seq<'a>>
let ZipN seqs = seqs |> Seq.map (fun x -> x |> Seq.map (fun y -> y)) |> ZipN_core
let zipn items = items |> Matrix.Generic.ofSeq |> Matrix.Generic.transpose
Or, if you really want to write it yourself:
let zipn items =
let rec loop items =
seq {
match items with
| [] -> ()
| _ ->
match zipOne ([], []) items with
| Some(xs, rest) ->
yield xs
yield! loop rest
| None -> ()
}
and zipOne (acc, rest) = function
| [] -> Some(List.rev acc, List.rev rest)
| []::_ -> None
| (x::xs)::ys -> zipOne (x::acc, xs::rest) ys
loop items
Since this seems to be the canonical answer for writing a zipn in f#, I wanted to add a "pure" seq solution that preserves laziness and doesn't force us to load our full source sequences in memory at once like the Matrix.transpose function. There are scenarios where this is very important because it's a) faster and b) works with sequences that contain 100s of MBs of data!
This is probably the most un-idiomatic f# code I've written in a while but it gets the job done (and hey, why would there be sequence expressions in f# if you couldn't use them for writing procedural code in a functional language).
let seqdata = seq {
yield Seq.ofList [ 1; 2; 3 ]
yield Seq.ofList [ 4; 5; 6 ]
yield Seq.ofList [ 7; 8; 9 ]
}
let zipnSeq (src:seq<seq<'a>>) = seq {
let enumerators = src |> Seq.map (fun x -> x.GetEnumerator()) |> Seq.toArray
if (enumerators.Length > 0) then
try
while(enumerators |> Array.forall(fun x -> x.MoveNext())) do
yield enumerators |> Array.map( fun x -> x.Current)
finally
enumerators |> Array.iter (fun x -> x.Dispose())
}
zipnSeq seqdata |> Seq.toArray
val it : int [] [] = [|[|1; 4; 7|]; [|2; 5; 8|]; [|3; 6; 9|]|]
By the way, the traditional matrix transpose is much more terse than #Daniel's answer. Though, it requires a list or LazyList that both will eventually have the full sequence in memory.
let rec transpose =
function
| (_ :: _) :: _ as M -> List.map List.head M :: transpose (List.map List.tail M)
| _ -> []
To handle having sub-lists of different lengths, I've used option types to spot if we've run out of elements.
let split = function
| [] -> None, []
| h::t -> Some(h), t
let rec zipN listOfLists =
seq { let splitted = listOfLists |> List.map split
let anyMore = splitted |> Seq.exists (fun (f, _) -> f.IsSome)
if anyMore then
yield splitted |> List.map fst
let rest = splitted |> List.map snd
yield! rest |> zipN }
This would map
let ll = [ [ 1; 2; 3 ];
[ 4; 5; 6 ];
[ 7; 8; 9 ] ]
to
seq
[seq [Some 1; Some 4; Some 7]; seq [Some 2; Some 5; Some 8];
seq [Some 3; Some 6; Some 9]]
and
let ll = [ [ 1; 2; 3 ];
[ 4; 5; 6 ];
[ 7; 8 ] ]
to
seq
[seq [Some 1; Some 4; Some 7]; seq [Some 2; Some 5; Some 8];
seq [Some 3; Some 6; null]]
This takes a different approach to yours, but avoids using some of the operations that you had before (e.g. Seq.skip, Seq.append), which you should be careful with.
I realize that this answer is not very efficient, but I do like its succinctness:
[[1;2;3]; [4;5;6]; [7;8;9]]
|> Seq.collect Seq.indexed
|> Seq.groupBy fst
|> Seq.map (snd >> Seq.map snd);;
Another option:
let zipN ls =
let rec loop (a,b) =
match b with
|l when List.head l = [] -> a
|l ->
let x1,x2 =
(([],[]),l)
||> List.fold (fun acc elem ->
match acc,elem with
|(ah,at),eh::et -> ah#[eh],at#[et]
|_ -> acc)
loop (a#[x1],x2)
loop ([],ls)
I want to calculate powerset of a set. Because I don't need the whole powerset at a time, it's better to generate it lazily.
For example:
powerset (set ["a"; "b"; "c"]) =
seq {
set [];
set ["a"];
set ["b"];
set ["c"];
set ["a"; "b"];
set ["a"; "c"];
set ["b"; "c"];
set ["a";"b"; "c"];
}
Since the result is a sequence, I prefer it in the above order. How can I do it in an idomatic way in F#?
EDIT:
This is what I'm going to use (based on BLUEPIXY's answer):
let powerset s =
let rec loop n l =
seq {
match n, l with
| 0, _ -> yield []
| _, [] -> ()
| n, x::xs -> yield! Seq.map (fun l -> x::l) (loop (n-1) xs)
yield! loop n xs
}
let xs = s |> Set.toList
seq {
for i = 0 to List.length xs do
for x in loop i xs -> set x
}
Thanks everyone for excellent input.
let rec comb n l =
match n, l with
| 0, _ -> [[]]
| _, [] -> []
| n, x::xs -> List.map (fun l -> x ::l) (comb (n - 1) xs) # (comb n xs)
let powerset xs = seq {
for i = 0 to List.length xs do
for x in comb i xs -> set x
}
DEMO
> powerset ["a";"b";"c"] |> Seq.iter (printfn "%A");;
set []
set ["a"]
set ["b"]
set ["c"]
set ["a"; "b"]
set ["a"; "c"]
set ["b"; "c"]
set ["a"; "b"; "c"]
val it : unit = ()
From F# for Scientists, slightly modified to be lazy
let rec powerset s =
seq {
match s with
| [] -> yield []
| h::t -> for x in powerset t do yield! [x; h::x]
}
Here's another approach, using maths instead of recursion:
let powerset st =
let lst = Set.toList st
seq [0..(lst.Length |> pown 2)-1]
|> Seq.map (fun i ->
set ([0..lst.Length-1] |> Seq.choose (fun x ->
if i &&& (pown 2 x) = 0 then None else Some lst.[x])))
How to write records just in time when the value for id of something is changing ? id for each record when ture->false and false->true for some list?
for example table
id value
1 0
2 0
2 0
2 0
1 0
2 1 --> the only changes here
2 1
1 0
2 0 --> and here (node with id 2 changed 1 -> 0 )
1 1 --> node with id 1 changed 0 -> 1
result table
2 1
2 0
1 1
my idea is not functional and a bit weird, I'm thinking about functional or linq way of making the same.
let oop = ref (filteredsq
|> Seq.distinctBy(fun (node,v,k) -> k)
|> Seq.map(fun (node,v,k) -> k, false )
|> Array.ofSeq )
[for (node,value,key) in filteredsq do
let i = ref 0
for (k,v) in !oop do
if key = k && value <> v then
(!oop).[!i] <- (k,value)
yield node
i := !i + 1 ]
Thank you
I think if you define a function like the following:
let getChanges f items =
items
|> Seq.map (fun x -> f x, x)
|> Seq.pairwise
|> Seq.choose (fun ((a, _), (b, x)) -> if a <> b then Some x else None)
Then you can do:
filteredsq
|> Seq.groupBy (fun (_, _, k) -> k)
|> Seq.collect (fun (_, items) ->
items
|> getChanges (fun (_, value, _) -> value)
|> Seq.map (fun (node, _, _) -> node))
|> Seq.toList
I'm not sure if I fully understand your question, but the following gives the right output according to your sample. The idea is to first filter out values that don't have the right key and then use Seq.pairwaise (as in jpalmer's solution) to find the places where the value changes:
let input = [ (1, 0); (2, 0); (2, 0); (2, 0); (1, 0); (2, 1); (2, 1); (1, 0); (2, 0) ]
let findValueChanges key input =
input
|> Seq.filter (fun (k, v) -> k = key) // Get values with the right key
|> Seq.pairwise // Make tuples with previous & next value
|> Seq.filter (fun ((_, prev), (_, next)) -> prev <> next) // Find changing points
|> Seq.map snd // Return the new key-value pair (after change)
If you wanted to find changes for all different keys, then you could use Seq.groupBy to find all possible keys (then you wouldn't need the first line in findValueChanges):
input
|> Seq.groupBy fst
|> Seq.map (fun (k, vals) -> findValueChanges k vals)
(For your input, there are no changes in values for the key 1, because the value is always 1, 0)
I would do something like
List
|> List.toSeq
|> Seq.pairwise
|> Seq.pick (fun ((fa,fb),(sa,sb)) -> if fb <> sb then Some(sa,sb) else None)
I'd just use an internal mutable dictionary to keep track of the last-seen values for each key and yield (key,value) when any value is different from the last value at that key:
let filterChanges (s:('a*'b) seq) =
let dict = new System.Collections.Generic.Dictionary<'a,'b>()
seq {
for (key,value) in s do
match dict.TryGetValue(key) with
| false,_ -> dict.[key] <- value
| true,lastValue ->
if lastValue <> value then
yield (key,value)
dict.[key] <- value
}
Test:
> filterChanges [(1,0);(2,0);(2,0);(2,0);(1,0);(2,1);(2,1);(1,0);(2,0);(1,1)];;
val it : seq<int * int> = seq [(2, 1); (2, 0); (1, 1)]
Updated
open System.Collections.Generic
let filter (acc:'a) (f:('a -> 'b -> bool * 'a)) (s:'b seq) =
let rec iter (acc:'a) (e:IEnumerator<'b>) =
match e.MoveNext() with
| false -> Seq.empty
| true -> match f acc e.Current with
| (true,newAcc) -> seq { yield e.Current; yield! iter newAcc e}
| (false,newAcc) -> seq { yield! iter newAcc e}
iter acc (s.GetEnumerator())
let skipUntilChange (f : 'a -> 'b) (s : 'a seq) =
s |> Seq.skip 1
|> filter (s |> Seq.head |> f)
(fun a b -> if a = f b then false,f b else true,f b)
[(1,0);(2,0);(2,0);(2,0);(1,0);(2,1);(2,1);(1,0);(2,0);]
|> Seq.mapi (fun c (i,v) -> (i,v,c))
|> Seq.groupBy (fun (i,v,c) -> i)
|> Seq.map (snd >> skipUntilChange (fun (_,v,_) -> v))
|> Seq.concat |> Seq.sortBy (fun (i,v,c) -> c)
|> Seq.map (fun (i,v,c) -> (i,v))
|> printfn "%A"
I was doing an exercise on F# Wiki Book on List (scroll to the bottom) to create a Pair method.
I was able to pair a integer list without problem but an F# exception was thrown for a string list. It is just too cryptic for me to decipher what the exception means for an F# beginner like me.
Here is my initial attempt to implementing Pair on fsi.exe
> let pair l =
- let rec loop acc = function
- | [] -> acc
- | (hd1 :: hd2 :: tl) -> loop ((hd1, hd2) :: acc) tl
- List.rev(loop [] l)
-
- printfn "%A" ([1..10] |> pair)
- printfn "%A" ([ "one"; "two"; "three"; "four"; "five" ] |> pair);;
let rec loop acc = function
-----------------------^
stdin(2,24): warning FS0025: Incomplete pattern matches on this expression.
For example, the value '[_]' will not be matched
val pair : 'a list -> ('a * 'a) list
[(1, 2); (3, 4); (5, 6); (7, 8); (9, 10)]
Microsoft.FSharp.Core.MatchFailureException:
Exception of type 'Microsoft.FSharp.Core.MatchFailureException' was thrown.
at FSI_0002.clo#2T.Invoke(List`1 acc, List`1 _arg1)
at FSI_0002.pair[T](List`1 l)
at <StartupCode$FSI_0002>.$FSI_0002._main()
stopped due to error
So Pair does work on integer version
and the function signature
val pair : 'a list -> ('a * 'a) list
indicates that Pair operates on a generic list.
Question: Then why would Pair not work on a string list?
[ANSWER] (my version)
Simply returning accumulated list for else case (_) did the trick.
And the warning is taken care of, as well.
let pair l =
let rec loop acc = function
// | [] -> acc
| (hd1 :: hd2 :: tl) -> loop ((hd1, hd2) :: acc) tl
| _ -> acc
List.rev(loop [] l)
printfn "%A" ([1..10] |> pair)
printfn "%A" ([ "one"; "two"; "three"; "four"; "five" ] |> pair)
[EDIT2] Well, I will also post my version of Unpair for completeness.
let unpair l = [for (a,b) in l do yield! a :: b :: []]
Here is somewhat flawed benchmarking using solution version against that of mine for 1 million item lists
#light
open System;
let pn l = printfn "%A" l
let duration f =
let startTime = DateTime.Now;
let returnValue = f()
let endTime = DateTime.Now;
printfn "Duration (ms): %f" (endTime - startTime).TotalMilliseconds
returnValue
let ll = [for a in 1..1000000 do yield (a)]
let tl = [for a in 1..1000000 do yield (a,a)]
let pair1 l =
let rec loop acc = function
| [] | [_] -> List.rev acc
| h1 :: h2 :: tl -> loop ((h1, h2) :: acc) tl
loop [] l
let unpair1 l =
let rec loop acc = function
| [] -> List.rev acc
| (h1, h2) :: tl -> loop (h2 :: h1 :: acc) tl
loop [] l
let pair2 l =
let rec loop acc = function
| (hd1 :: hd2 :: tl) -> loop ((hd1, hd2) :: acc) tl
| _ | [_] -> acc
List.rev(loop [] l)
let unpair2 l = [for (a,b) in l do yield! a :: b :: []]
pn(duration (fun() -> ll |> pair1))
pn(duration (fun() -> tl |> unpair1))
pn(duration (fun() -> ll |> pair2))
pn(duration (fun() -> tl |> unpair2))
Benchmark Result:
Solution version
PAIR -> Duration (ms): 255.000000
UNPAIR -> Duration (ms): 840.000000
My version
PAIR -> Duration (ms): 220.000000
UNPAIR -> Duration (ms): 1624.000000
I don't think your version of Pair would work on a list of an odd number of anything. You happen to test an even number of ints and a odd number of strings. I think your second argument to match implies a list of at least two members. So you break off 2 break off 2 and get to a list with 1 element and none of your conditions match.
[_] is a 1 item list with anything in it. You must provide a predicate that matches it.