使用部分功能短路列表映射 [英] Short circuiting a list mapping with a partial function

查看:64
本文介绍了使用部分功能短路列表映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我制作了一个名为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

如其文档中所述,其目的是使用部分函数来映射列表,如果缺少更好"的单词,则在完全"映射不可能"的情况下简短地求知.

The purpose, as described in its documentation, is to map a list using a partial function, and short curcuit if a "full" mapping isn't "possible", for lack of better words.

以下是如何使用它的示例:

使用此功能...

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对整数列表进行全有或全无的映射,如下所示:

...we can now do an all-or-nothing mapping of a list of integers with tryMap like this:

> 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

随后,例如,计算这些值的总和将很容易-如果可以全部进行计算,那就是

Afterwards it would be easy to, for example, calculate the sum of these values - if they could all be computed, that is.

现在,我的问题是:

是否有更简单/更干净的方法来实现此 tryMap 函数?(当然,除了不那么冗长之外::-P)我感觉可以使用列表表达式,也许表达式(来自FSharpx)或两者的结合来完成一些聪明的事情,但是目前我还不太清楚该怎么做.:-/

Is there a simpler/cleaner way to implement this tryMap function? (Besides not being so darn verbose, of course. :-P) I have a feeling something smart could be done using list expressions, maybe-expressions (from FSharpx) or maybe a combination of both, but I can't quite figure out how, at the moment. :-/

PS:如果该功能的名称比'tryMap'更好,请随时发表评论.:-)

更新1:

我想出了这个版本,该版本与我的初衷非常接近,只是它不会短路.:-/

I have come up with this version, which is very close to what I had in mind, except it sadly doesn't short-circuit. :-/

let tryMap : ('T -> 'U option) -> 'T list -> 'U list option = 
    fun mapping list -> maybe { for value in list do return mapping value }

注意:这使用FSharpx的may-表达式.

更新2:

感谢 Tomas Petricek ,我有了一个替代方案:

Thanks to Tomas Petricek, I got an idea for an alternative:

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 将为 true ,其余元素将不会被映射.之后,如果 quit true ,则部分映射的列表将被丢弃,并返回 None .如果它从未映射到 None ,则它将最终构建出用于构造映射列表的延续.

This function stops mapping after it maps to its first None value. When that happens, quit will be true, and the remaining elements won't be mapped. Afterwards, if quit is true, the partly mapped list is discarded and None is returned. If it never maps to None, it will have ended up building a continuation for constructing the mapped list.

尽管它仍然很大,但现在它仅发生轻度"短路,从某种意义上说,它不再尝试映射列表,但仍然遍历列表,因为这是折叠的工作方式.:-/

It's still quite large though, and now it only does a "light" short circuit, in the sense that it stops trying to map the list, but it still traverses it, since that's how a fold works. :-/

推荐答案

基于Daniel和SørenDebois的回答和评论,我提出了以下建议:

Based on answers and comments by Daniel and Søren Debois, I've come up with the following:

let tryMap f xs =
    let rec loop ys = function
        | []    -> maybe { return List.rev ys }
        | x::xs -> maybe { let! y = f x in return! loop (y::ys) xs }
    loop [] xs

我不太确定它是否为尾递归,因为我不是计算(或在这种情况下:也许)表达式的内部专家.

I'm not quite sure if it's tail-recursive or not though, since I'm not a total expert in the inner workings of computation- (or in this case specifically: maybe-) expressions.

你们怎么看?:-)

这篇关于使用部分功能短路列表映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆