如何在gen计算表达式中实现Gen.filter的等效功能?
在FsCheck的gen计算表达式内实现过滤逻辑
原代码示例:
type Status = Pending | Activated | Deactivated type Item = { Id: int; Status: Status } let nonPendingItemGen = Arb.generate<Item> |> Gen.filter (fun item -> item.Id > 0 && item.Status <> Pending)
问题
能否将这段代码重构为在gen计算表达式内部实现过滤逻辑?
- 尝试使用if语句时因分支类型不匹配失败;
- 已找到通过生成序列再过滤的方案,但不够优雅。
解决方案
可以直接在gen计算表达式内通过递归重试的方式实现过滤,写法如下:
type Status = Pending | Activated | Deactivated type Item = { Id: int; Status: Status } let nonPendingItemGen = gen { let! item = Arb.generate<Item> if item.Id > 0 && item.Status <> Pending then return item else return! nonPendingItemGen // 不符合条件就递归重试当前生成器 }
这种写法和Gen.filter的底层逻辑一致,都是通过重试来筛选符合条件的生成值。需要注意的是,如果过滤条件过于严苛,可能会导致FsCheck生成值超时或失败,这是过滤类生成器的共性限制。
相比生成序列再过滤的方案,这种递归重试的写法更直观,完全在gen计算表达式内部完成逻辑,无需额外的序列操作。
内容的提问来源于stack exchange,提问作者Romain Deneau
相关产品推荐
相关产品推荐

