我目前正以类似的方式生成序列:
migrators
|> Seq.map (fun m -> m())
migrator
函数最终返回一个受歧视的联合,如:
type MigratorResult =
| Success of string * TimeSpan
| Error of string * Exception
我想在遇到第一个map
后停止Error
,但我需要在最终序列中加入Error
。
我有以下内容向用户显示最终消息
match results |> List.rev with
| [] -> "No results equals no migrators"
| head :: _ ->
match head with
| Success (dt, t) -> "All migrators succeeded"
| Error (dt, ex) -> "Migration halted owing to error"
所以我需要:
Error
我很欣赏除了map
之外可能会有不同的序列方法来做这件事,我是F#的新手并且在线搜索还没有产生任何东西!
答案 0 :(得分:6)
我想这里有多种方法,但一种方法是使用展开:
migrators
|> Seq.unfold (fun ms ->
match ms with
| m :: tl ->
match m () with
| Success res -> Some (Success res, tl)
| Error res -> Some (Error res, [])
| [] -> None)
|> List.ofSeq
注意最后的List.ofSeq
,这就是实现序列的目的。另一种方法是使用序列理解,有些人可能会说它会产生更清晰的代码。
答案 1 :(得分:3)
Tomaš暗示的丑陋事物是1)可变状态,2)操纵底层枚举器。返回到包括谓词保持时的高阶函数将如下所示:
module Seq =
let takeUntil pred (xs : _ seq) = seq{
use en = xs.GetEnumerator()
let flag = ref true
while !flag && en.MoveNext() do
flag := not <| pred en.Current
yield en.Current }
seq{1..10} |> Seq.takeUntil (fun x -> x % 5 = 0)
|> Seq.toList
// val it : int list = [1; 2; 3; 4; 5]
对于您的特定应用,您将DU的情况映射到布尔值。
(migrators : seq<MigratorResult>)
|> Seq.takeUntil (function Success _ -> false | Error _ -> true)
答案 2 :(得分:2)
我认为@scrwtp的答案可能是最好的方法,如果您的输入相当小(并且您可以将其转换为F#列表以使用模式匹配)。我还要再添加一个版本,当您的输入只是一个序列并且您不想将其转换为列表时,该版本可以正常工作。
基本上,你想要做的事情几乎就像Seq.takeWhile
,但它最后会给你一个额外的项目(谓词失败的那个)。
要使用更简单的示例,以下内容将返回序列中的所有数字,直到可被5整除的数字:
let nums = [ 2 .. 10 ]
nums
|> Seq.map (fun m -> m % 5)
|> Seq.takeWhile (fun n -> n <> 0)
所以,你基本上只需要提前看一个元素 - 为此,你可以使用Seq.pairwise
来提供序列中的当前和下一个元素&#34;
nums
|> Seq.map (fun m -> m % 5)
|> Seq.pairwise // Get sequence of pairs with the next value
|> Seq.takeWhile (fun (p, n) -> p <> 0) // Look at the next value for test
|> Seq.mapi (fun i (p, n) -> // For the first item, we return both
if i = 0 then [p;n] else [n]) // for all other, we return the second
|> Seq.concat
这里唯一令人遗憾的是,您需要使用mapi
和concat
再次展平序列。
这不是很好,所以要做的好事就是定义你自己的高阶函数,如Seq.takeUntilAfter
,它封装了你需要的行为(并隐藏了所有丑陋的东西)。那么你的代码可以只使用这个功能,看起来很漂亮&amp;可读(您可以尝试其他实现方法)。