Из ваших комментариев стало ясно, что вы хотели бы избежать итерации по всей последовательности, останавливаясь, как только вы встретите первый Ok
.
Что ж, последовательности уже делают это по умолчанию (они ленивые ), а функция scan
сохраняет это свойство. Давайте проверим:
let mySeq = seq {
for i in 0..3 do
printfn "Returning %d" i
yield i
}
mySeq |> Seq.toList |> ignore
> Returning 0
> Returning 1
> Returning 2
> Returning 3
mySeq |> Seq.take 2 |> Seq.toList |> ignore
> Returning 0
> Returning 1
mySeq
|> Seq.scan (fun _ x -> printfn "Scanning %d" x) ()
|> Seq.take 3
|> Seq.toList |> ignore
> Returning 0
> Scanning 0
> Returning 1
> Scanning 1
Смотрите: мы никогда не видим «Возвращение 2» и «Возвращение 3» после scan
. Это потому, что мы не повторяем всю последовательность, а только ту часть, которая нам нужна, как определено Seq.take 3
.
Но то, что делает заставляет полную итерацию в вашем коде, это Seq.last
. В конце концов, чтобы получить последний элемент, вам нужно перебрать всю последовательность, другого пути нет.
Но что вы можете сделать, так это остановить итерацию, когда вам нужно, через Seq.takeWhile
. Эта функция принимает предикат и возвращает только элементы, для которых предикат равен true
, за исключением первого, который дает false
:
mySeq |> Seq.takeWhile (fun x -> x < 2) |> Seq.toList |> ignore
> Returning 0
> Returning 1
> Returning 2
> val it : int list = [0; 1]
Трудность в вашем случае заключается в том, что вам также необходимо вернуть элемент, нарушающий предикат. Для этого вы можете использовать небольшой прием: оставить в своем свернутом состоянии специальный флаг stop: bool
, изначально установить его на false
и переключиться на true
на элементе, который сразу же следует за тем, который вам нужен. остановиться. Чтобы сохранить такое состояние, я собираюсь использовать запись:
let st0 = {| prev = Error []; stop = false |}
let acc (s: {| prev: Result<_,string>; stop: bool |}) x =
match s.prev, x with
| Ok _, _ -> {| s with stop = true |} // Previous result was Ok => stop now
| _, Ok _ -> {| s with prev = x |} // Don't stop, but remember the previous result
| Error a, Error b -> {| s with prev = Error (a @ b) |}
sourceSequence
|> Seq.scan acc st0
|> Seq.takeWhile (fun s -> not s.stop)
|> Seq.last
|> (fun s -> s.prev)
PS также обратите внимание, что в конкатенации списка F # это @
, а не ++
. Вы едете из Haskell?