2009-08-08 5 views
8
 
let aBunch = 1000 
let offset = 0 

let getIt offset = 
    MyIEnumerable 
    |> Seq.skip aBunch * offset 
    |> Seq.take aBunch 
    |> Seq.iter (.. some processing ...) 

Calling Getit() con diversi offset alla fine mi dà un'eccezione 'operazione non valido' con informazioni aggiuntive che 'la sequenza di ingresso aveva elementi sufficienti'Calling Seq.skip e Seq.take in F #

provo per capire il motivo per cui, in quanto sia il Seq.Skip e Seq.take non generano un'eccezione in base alla documentazione in linea FSharp Collections

Versione: (Visual Studio 2010) Beta 1

+2

La documentazione non dice nulla sulle eccezioni; i documenti sono incompleti. Presenterò un bug di documento. – Brian

risposta

6

sia Seq.skip e Seq.take getterà questa eccezione se chiamata ed con un valore maggiore della sequenza. È possibile controllare il codice sorgente in Seq.fs capire perché:

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 } 
20

So che questa è una vecchia questione, ma nel caso in cui qualcuno si imbatte in questo in un cercare il modo in cui ho fatto:

Puoi utilizzare Seq.truncate se si vuole al massimo n articoli. Non genererà un'eccezione se sono disponibili meno di n articoli.

1

Per un senza eccezioni skip è possibile aggiungere la propria versione al modulo Seq in questo modo:

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 
     } 

Combinato con Seq.truncate (che è un senza eccezioni Seq.take equivalente - ci vorrà tanto articoli sono disponibili senza gettare un eccezione).

[1..10] 
|> Seq.skipSafe 20 
|> Seq.truncate 5 

(* returns empty seq *) 
1

Ecco un'implementazione leggermente più corto "skipSafe" utilizzando costruito nelle funzioni:

module Seq = 
    let skipSafe num = 
     Seq.zip (Seq.initInfinite id) 
     >> Seq.skipWhile (fun (i, _) -> i < num) 
     >> Seq.map snd 

Oppure, se si desidera inline solo nella tua pipeline corrente direttamente, sostituire

|> Seq.skip num 

con

|> Seq.zip (Seq.initInfinite id) 
|> Seq.skipWhile (fun (i, _) -> i < num) 
|> Seq.map snd 
0
module Seq = 
    let trySkip count source = 
     source |> Seq.indexed |> Seq.filter(fst >> (<=) count) |> Seq.map snd