как прервать последовательность в F#?

#f#

Вопрос:

У меня есть такой раздел:

 let data =
    seq {
        for i in source do
            do some stuff...
            yield result
    }
 

Но я хотел бы превратить его в объект результата, и во время итерации у меня возникают некоторые условия, которые означают ошибку. Например:

 let data =
    seq {
        for i in source do
            do some stuff...
            if a > b then .. let's abort everything and return error
            yield result
    } |> Ok
 

Расчеты довольно дороги, поэтому он предпочел бы не обертывать доходность опционом / результатом и разобраться с этим позже, если я смогу прерваться раньше.

Или, если я сделаю это, как я могу повторять, пока он не завершится или не вернет ошибку?

Как это можно сделать?

Ответ №1:

Вы можете подключиться Seq.takeWhile и разобраться там Seq , следует ли прерывать:

 let data =
    source
    |> Seq.map (fun s -> ... do some stuff ...)
    |> Seq.takeWhile (fun r -> ... return false if error ...)
 

Seq<T> это IEnumerable<T> так лениво, вычисления выполняются, когда из него извлекаются новые элементы.

Редактировать:

Чтобы узнать, была ли ошибка или мы достигли конца раздела, я бы пересек его, используя сгиб, чтобы раздел «исходные» элементы вычислялся на лету, и если есть ошибка, затем пройдите через остальные, не выполняя вычисления. Вот пример:

 let source = seq { 
  1
  2
  3
  4 }

let expensiveCalc item =
  printfn "expensiveCalc called with %i" item
  if item > 2 then Error (sprintf "%i is too high" item)
  else Ok item

let traverse valuesResult newSourceItem =
  valuesResult
  |> Result.bind (fun values ->
      expensiveCalc newSourceItem
      |> Result.map (fun newResult -> newResult::values))

let endResult =
  source
  |> Seq.fold traverse (Ok [])

match endResult with
| Ok values -> printfn "%A" values
| Error error -> printfn "%s" error
 

Как вы можете видеть, любое число, большее 2, считается ошибкой, и при запуске оно перестает звонить expensiveCalc после первой ошибки.

Комментарии:

1. если мои результаты вернут результат, я могу использовать время ожидания, чтобы продолжать извлекать данные до тех пор, пока не будет достигнута ошибка, но как бы я отличил конец последовательности от достигнутой ошибки?

2. @Thomas Я бы тогда свернул исходный код и рассчитал на лету. Недостатком этого, я думаю, является то, что весь исходный код истощен. См.раздел правка.

3. значит, в итоге вы делаете все расчеты? например, завернуть все в опцию и проверить, нет ли ее в конце, и если она не найдена, запустите, выберите, чтобы получить данные

4. @Thomas Нет, вычисления выполняются только до тех пор, пока не произойдет первая ошибка, но весь исходный код (до вычислений) сливается. Запустите второй пример кода выше, и вы увидите, что «expensiveCalc вызывается с %i» печатается только для чисел 1, 2 и 3 (3-ошибка), 4 никогда не вычисляется.