因此,我制作了这个名为 tryMap 的函数,如下所示:
/// tryMap, with failure and success continuations.
let rec tryMapC : 'R -> ('U list -> 'R) -> ('T -> 'U option) -> ('T list) -> 'R =
fun failure success mapping list ->
match list with
| [] -> success []
| head::tail ->
match mapping head with
| None -> failure
| Some result ->
let success = (fun results -> result::results |> success)
tryMapC failure success mapping tail
/// <summary>
/// Attempts to map a list with a partial function.
/// <para/>
/// If a value which maps to None is encountered,
/// the mapping stops, and returns None.
/// <para/>
/// Else, Some(list), containing the mapped values, is returned.
/// </summary>
let tryMap : ('T -> 'U option) -> 'T list -> 'U list option =
fun mapping list ->
tryMapC None Some mapping list
如其文档中所述,其目的是使用部分函数映射列表,如果“完整”映射不是“可能”,则使用简短的 curcuit,因为缺少更好的词。
这是如何使用它的示例:
鉴于此功能...
let tryFac n =
do printf "The factorial of %d" n
if n < 0 then
do printf " cannot be computed.\n"
None
else
let result = (List.fold (*) 1 [1..n])
do printf " is %d\n" result
Some result
...我们现在可以使用 tryMap 对整数列表进行全有或全无映射,如下所示:
> let all = tryMap tryFac [1..5];;
The factorial of 1 is 1
The factorial of 2 is 2
The factorial of 3 is 6
The factorial of 4 is 24
The factorial of 5 is 120
val all : int list option = Some [1; 2; 6; 24; 120]
> let nothing = tryMap tryFac [1;2;-3;4;5];;
The factorial of 1 is 1
The factorial of 2 is 2
The factorial of -3 cannot be computed.
val nothing : int list option = None
之后,例如,计算这些值的总和将很容易——如果它们都可以计算出来的话。
现在,我的问题是:
有没有更简单/更干净的方法来实现这个tryMap函数?(当然,除了不那么冗长之外。:-P)我觉得可以使用列表表达式、maybe-expressions(来自 FSharpx)或两者的组合来做一些聪明的事情,但我不太明白目前如何。:-/
PS:如果您对此功能有一个比“tryMap”更好的名称,请不要犹豫发表评论。:-)
更新1:
我想出了这个版本,它非常接近我的想法,但遗憾的是它没有短路。:-/
let tryMap : ('T -> 'U option) -> 'T list -> 'U list option =
fun mapping list -> maybe { for value in list do return mapping value }
注意:这使用了 FSharpx 的可能表达式。
更新 2:
感谢Tomas Petricek
,我想到了一个替代方案:
let tryMap (mapping : 'T -> 'U option) (list : 'T list) : 'U list option =
List.fold
(
fun (cont,quit) value ->
if quit then
(cont,quit)
else
match mapping value with
| None -> (cont,true)
| Some r -> ((fun rs -> (r::rs)) >> cont,quit)
)
(id,false)
list
|> (fun (cont,quit) -> if quit then None else Some (cont []))
此函数在映射到其第一个None
值后停止映射。发生这种情况时,quit
will be true
,其余元素将不会被映射。之后,如果quit
是true
,则丢弃部分映射的列表并None
返回。如果它从不映射到None
,它将最终构建一个用于构建映射列表的延续。
虽然它仍然很大,现在它只做了一个“轻微”的短路,因为它停止尝试映射列表,但它仍然遍历它,因为这就是折叠的工作方式。:-/