-
-
Save killerswan/6271720 to your computer and use it in GitHub Desktop.
| module Seq2 = | |
| // split into head and tail subsequences | |
| let split nn (xs: seq<'UU>) : seq<'UU> * seq<'UU> = | |
| let head = | |
| Seq.truncate nn xs |> Seq.cache | |
| let tail = | |
| if (Seq.length head < nn) then | |
| Seq.empty | |
| else | |
| Seq.skip nn xs |> Seq.cache | |
| (head, tail) | |
| let split' nn (xs: seq<'UU>) : seq<'UU> * seq<'UU> = | |
| let en = xs.GetEnumerator() | |
| let next, nn = ref true, ref nn | |
| seq { | |
| // start enumerating | |
| next:= en.MoveNext() | |
| while !next && !nn > 0 do | |
| yield! try [en.Current] with _ -> [] | |
| next := en.MoveNext() | |
| decr nn | |
| }, | |
| seq { | |
| while !next do | |
| yield en.Current | |
| next:= en.MoveNext() | |
| } | |
| // return chunks of a sequence, each with nn items | |
| // (the last chunk may be partially full) | |
| let chunk_ split nn (xs: seq<'UU>) : seq<seq<'UU>> = | |
| let empt zs = | |
| //printf "«" | |
| let e = Seq.isEmpty zs | |
| //printf "» " | |
| e | |
| let gen ys = | |
| //if Seq.isEmpty ys then | |
| if empt ys then | |
| None | |
| else | |
| Some(split nn ys) | |
| Seq.unfold gen xs | |
| // export... | |
| let chunk nn (xs: seq<'UU>) : seq<seq<'UU>> = | |
| chunk_ split' nn xs | |
| let run () = | |
| printfn "%A" <| split 2 [1] | |
| printfn "%A" <| split 4 [1..4] | |
| printfn "%A" <| split 4 [1..7] | |
| printfn "%A" <| chunk_ split 2 [1] | |
| printfn "%A" <| chunk_ split 2 [1..4] | |
| printfn "%A" <| chunk_ split 4 [1..7] | |
| printfn "" | |
| (* | |
| (seq [1], seq []) | |
| (seq [1; 2; 3; 4], seq []) | |
| (seq [1; 2; 3; 4], seq [5; 6; 7]) | |
| seq [seq [1]] | |
| seq [seq [1; 2]; seq [3; 4]] | |
| seq [seq [1; 2; 3; 4]; seq [5; 6; 7]] | |
| *) | |
| printfn "%A" <| split' 2 [1] | |
| printfn "%A" <| split' 4 [1..4] | |
| printfn "%A" <| split' 4 [1..7] | |
| printfn "%A" <| chunk_ split' 2 [1] | |
| printfn "%A" <| chunk_ split' 2 [1..4] | |
| printfn "%A" <| chunk_ split' 4 [1..7] | |
| printfn "" | |
| (* | |
| (seq [1], seq []) | |
| (seq [1; 2; 3; 4], seq []) | |
| (seq [1; 2; 3; 4], seq [5; 6; 7]) | |
| seq [seq [1]] | |
| seq [seq [1; 2]; seq [3; 4]] | |
| seq [seq [1; 2; 3; 4]; seq [5; 6; 7]] | |
| *) | |
| let range a b = seq { for i in a .. b -> printf "!%d " i; i } | |
| let _print xs = printf "seq ["; Seq.iter (printf "%d ") xs; printf "] " | |
| let print (head,tail) = printf "( "; _print head; printf ", "; _print tail; printfn ")" | |
| let print2 xss = Seq.iter _print xss; printfn "" | |
| print <| split 2 (range 1 1) | |
| print <| split 4 (range 1 4) | |
| print <| split 4 (range 1 7) | |
| print2 <| chunk_ split 2 (range 1 1) | |
| print2 <| chunk_ split 2 (range 1 4) | |
| print2 <| chunk_ split 4 (range 1 7) | |
| printfn "" | |
| (* | |
| !1 ( seq [1 ] , seq [] ) | |
| !1 !2 !3 !4 ( seq [1 2 3 4 ] , seq [!1 !2 !3 !4 ] ) | |
| !1 !2 !3 !4 ( seq [1 2 3 4 ] , seq [!1 !2 !3 !4 !5 5 !6 6 !7 7 ] ) | |
| !1 !1 seq [1 ] | |
| !1 !1 !2 seq [1 2 ] !1 !2 !3 !4 seq [3 4 ] | |
| !1 !1 !2 !3 !4 seq [1 2 3 4 ] !1 !2 !3 !4 !5 !6 !7 seq [5 6 7 ] | |
| *) | |
| print <| split' 2 (range 1 1) | |
| print <| split' 4 (range 1 4) | |
| print <| split' 4 (range 1 7) | |
| print2 <| chunk_ split' 2 (range 1 1) | |
| print2 <| chunk_ split' 2 (range 1 4) | |
| print2 <| chunk_ split' 4 (range 1 7) | |
| printfn "" | |
| (* | |
| ( seq [!1 1 ] , seq [] ) | |
| ( seq [!1 1 !2 2 !3 3 !4 4 ] , seq [] ) | |
| ( seq [!1 1 !2 2 !3 3 !4 4 !5 ] , seq [5 !6 6 !7 7 ] ) | |
| !1 seq [!1 1 ] | |
| !1 seq [!1 1 !2 2 !3 ] seq [3 !4 4 ] | |
| !1 seq [!1 1 !2 2 !3 3 !4 4 !5 ] seq [5 !6 6 !7 7 ] | |
| *) | |
| run () |
@nagat01 Where do the zeroes come from? Your split is very lazy, but also isn't finished yet...
I love the way your range function makes it clear when a seq is being expanded, though!
@nagat01 I've fixed the zeroes: those were getting printed when you didn't first call a .MoveNext() before reading. And even playing around with Seq.cache, I can't make split as efficient as split' yet. :D
(See above!)
I couldn't find the zeroes.. You would have better eyeballs than mine :)
And thank you for correcting and investigating it.
BTW, Seq.cache would significantly speed up the nested call of Seq.skip and "Seq.length head" is so efficient than calculating length of whole the sequence (I didn't notice this).
Then, it would have practically a good performance.
And the code is concise.
Then I'll use your split when I need it :)
I wrote the same thing in other way.
https://gist.github.com/nagat01/6280003
In the above gist, Seq.split' has no strict evaluation over the sequence because Seq.length isn't used in the function.
And Seq.take in Seq.split doesn't throws the exception because the length is checked before calling it.
I admit that my Seq.split' is too long..
But I spent so long time to eliminate strict evaluation in the split function, then I post it here..