Вызов Seq.skip и Seq.take в F # - PullRequest
       35

Вызов Seq.skip и Seq.take в F #

9 голосов
/ 08 августа 2009
let aBunch = 1000
let offset = 0

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

Вызов getIt () с различными смещениями в конечном итоге дает мне исключение «Недопустимая операция» с дополнительной информацией о том, что «во входной последовательности было недостаточно элементов»

Я пытаюсь понять, почему, поскольку и Seq.Skip, и Seq.take не генерируют исключение согласно онлайн-документации FSharp Collections

Версия: (Visual Studio 2010) Beta 1

Ответы [ 5 ]

24 голосов
/ 05 марта 2010

Я знаю, что это старый вопрос, но в случае, если кто-то сталкивается с этим в поиске, как я:

Вы можете использовать Seq.truncate , если хотите максимум n элементов. Он не выдаст исключение, если доступно менее n элементов.

7 голосов
/ 08 августа 2009

И Seq.skip, и Seq.take сгенерируют это исключение, если оно вызывается со значением, превышающим последовательность. Вы можете проверить исходный код в Seq.fs, чтобы понять, почему:

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 }
3 голосов
/ 15 августа 2014

Для без исключения skip вы можете добавить свою собственную версию в модуль Seq следующим образом:

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 
        }

В сочетании с Seq.truncate (что является безусловным Seq.take эквивалентом - потребуется столько же предметов, сколько можно получить без исключения).

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

(* returns empty seq *)
2 голосов
/ 02 марта 2016

Вот немного более короткая реализация "skipSafe" с использованием встроенных функций:

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

Или, если вы хотите напрямую вставить его в текущий конвейер, замените

|> Seq.skip num

с

|> Seq.zip (Seq.initInfinite id)
|> Seq.skipWhile (fun (i, _) -> i < num)
|> Seq.map snd
1 голос
/ 12 апреля 2016
module Seq = 
    let trySkip count source  =
        source |> Seq.indexed |> Seq.filter(fst >> (<=) count) |> Seq.map snd
...