Reputation: 545
let aBunch = 1000 let offset = 0 let getIt offset = MyIEnumerable |> Seq.skip aBunch * offset |> Seq.take aBunch |> Seq.iter ( .. some processing ...)
Calling getIt() with different offsets eventually gives me an 'Invalid operation' exception with additional info that 'the input sequence had insufficient elements'
I try to understand why, as both the Seq.Skip and Seq.take do not generate an exception according to the online documentation FSharp Collections
Version: (Visual Studio 2010) Beta 1
Upvotes: 12
Views: 2914
Reputation: 4210
For skip
, you can easily define a trivial skipSafe
with (presumably) optimal performance by delegating to LINQ's Skip
:
open System.Linq
let skipSafe count (source: seq<'a>) = source.Skip(count)
For take
, as the other answers say, you can just use Seq.truncate
, or optionally alias it:
let takeSafe count source = Seq.truncate count source
Upvotes: 0
Reputation: 21
module Seq =
let trySkip count source =
source |> Seq.indexed |> Seq.filter(fst >> (<=) count) |> Seq.map snd
Upvotes: 2
Reputation: 31
Here's a slightly shorter "skipSafe" implementation using built in functions:
module Seq =
let skipSafe num =
Seq.zip (Seq.initInfinite id)
>> Seq.skipWhile (fun (i, _) -> i < num)
>> Seq.map snd
Or if you wish to just inline it into your current pipeline directly, replace
|> Seq.skip num
with
|> Seq.zip (Seq.initInfinite id)
|> Seq.skipWhile (fun (i, _) -> i < num)
|> Seq.map snd
Upvotes: 3
Reputation: 1315
For an exceptionless skip
you can add your own version to the Seq module like this:
module Seq =
let skipSafe (num: int) (source: seq<'a>) : seq<'a> =
seq {
use e = source.GetEnumerator()
let idx = ref 0
let loop = ref true
while !idx < num && !loop do
if not(e.MoveNext()) then
loop := false
idx := !idx + 1
while e.MoveNext() do
yield e.Current
}
Combined with Seq.truncate
(which is an exceptionless Seq.take
equivalent - it will take as much items are available without throwing an exception).
[1..10]
|> Seq.skipSafe 20
|> Seq.truncate 5
(* returns empty seq *)
Upvotes: 3
Reputation: 28765
I know this is an old question, but in case someone comes across this in a search the way I did:
You can use Seq.truncate if you want at most n items. It won't throw an exception if fewer than n items are available.
Upvotes: 29
Reputation: 9855
Both Seq.skip and Seq.take will throw this exception if called with a value larger than the sequence. You can check the source code in Seq.fs to see why:
let skip count (sequence: seq<_>) =
{ use e = sequence.GetEnumerator()
let latest = ref (Unchecked.defaultof<_>)
let ok = ref false
for i in 1 .. count do
if not (e.MoveNext()) then
raise <| System.InvalidOperationException "the input sequence had insufficient elements"
while e.MoveNext() do
yield e.Current }
let take count (sequence : seq<'T>) =
if count < 0 then invalidArg "count" "the number of elements to take may not be negative"
(* Note: don't create or dispose any IEnumerable if n = 0 *)
if count = 0 then empty else
{ use e = sequence.GetEnumerator()
for i in 0 .. count - 1 do
if not (e.MoveNext()) then
raise <| System.InvalidOperationException "the input sequence had insufficient elements"
yield e.Current }
Upvotes: 7