如何直接重用F#中从另一个函数返回的函数

时间:2014-03-22 19:47:17

标签: f#

我目前正在开发一个项目,我必须根据一些规则创建很多函数,然后再重用这些函数。为此,我开始学习F#,在F#中做起来似乎比在C#中更自然。

我设法创建了它们应该存在的功能,但是在重用它们时我遇到了一些问题。下面是一个非常简单的示例,以显示我正在尝试做的事情。

let choice = 2

let fCreator (x:float) =
    match choice with
    |1 ->  x * x
    |2 ->  x * 10.0
    |3 ->  x + 20.0
    |_ ->  x

我有一个全局变量选择,根据该选择,在fCreator中选择并返回x的变换。通过调用它可以正常工作,例如

let result1 = fCreator 1.0

但问题是我必须使用所选的转换(在这种情况下为x * 10)来获得许多不同的输入以及以这种方式使用它

let result2 = fCreator 2.0
let result3 = fCreator 3.0
.
.
.

然后整个选择过程再次完成,这在这里没什么大不了的,但我的实际选择功能要复杂得多,需要一些时间来计算。

是否可以调用fCreator,获取并保存所选转换,然后直接使用它?像

这样的东西
let selectedFunction x:float = fCreator x:float

然后

selectedFunction x:float = x * 10

我知道上面的内容不会这样,但我希望你能理解我想要做的事情。那么selectedFunction应该是从fCreator中选择的转换。

我对F#很陌生,所以我可能会忽略一些显而易见的东西,但是根据F#中的函数缓存,我仔细阅读了几个Q& As。但据我所知,无论是记忆还是懒惰的评估都无法满足我的需求。

我还尝试将fCreator写为void函数,在body中将x作为声明但未初始化的变量,然后返回带有该变量的转换。但它并没有完全奏效,而且我知道也不是在F#中做正确的方法。

我希望有人能在这里给我一个提示。

4 个答案:

答案 0 :(得分:2)

不是依赖于全局值,而是定义一个返回另一个函数的函数:

let selectCreator choice =
    match choice with
    | 1 -> fun x -> x * x
    | 2 -> fun x -> x * 10.0
    | 3 -> fun x -> x + 20.0
    | _ -> id

这是一个返回所需功能的函数。它有签名int -> (float -> float)。您可以将它绑定到特定的选项,如下所示:

let creator = selectCreator 2

现在,您可以将creator用于各种值:

> creator 2.0;;
val it : float = 20.0
> creator 3.0;;
val it : float = 30.0

仅在您调用selectCreator时才会进行选择;每次调用creator时,只需调用返回的函数即可。这可以通过对selectCreator进行以下修改来说明:

let selectCreator choice =
    match choice with
    | 1 ->
        printf "1 selected"
        fun x -> x * x
    | 2 ->
        printf "2 selected"
        fun x -> x * 10.0
    | 3 ->
        printf "3 selected"
        fun x -> x + 20.0
    | _ ->
        printf "Default selected"
        id

虽然在功能编程中,我们不喜欢副作用,但它是一种很好的方式来证明这是按预期工作的。这是FSI会话的日志:

> let creator = selectCreator 2;;
2 selected
val creator : (float -> float)

> creator 4.0;;
val it : float = 40.0

> creator 5.0;;
val it : float = 50.0

请注意,它仅在选择creator时打印出选择,而不是在调用时。

此方法还具有以下优势:您可以在运行时改变主意,并选择其他功能:

> let creator3 = selectCreator 3;;
3 selected
val creator3 : (float -> float)

> creator3 4.0;;
val it : float = 24.0
> creator3 5.0;;
val it : float = 25.0

答案 1 :(得分:1)

如果选择计算一次然后总是相同,那么你可以这样做:

let choice = 2

let fCreator =
    match choice with
    | 1 -> fun x -> x * x
    | 2 -> fun x -> x * 10
    | 3 -> fun x -> x + 20
    | _ -> x

这将返回一个完全不同的函数,具体取决于choice的值。

答案 2 :(得分:1)

你有正确的想法,重用一个“从另一个回来”的功能。你可以这样做:

let mkCreator choice = function
    | 1 -> fun x -> x * x
    | 2 -> ( *) 10.0
    | 3 -> (+) 20.0
    | _ -> id

let fCreator = mkCreator 2
fCreator 20.0               (* val it = 200.0 : float *)

这使“昂贵的”mkCreator调用只发生一次。


另一个答案提出以下建议:

let mkCreator choice x = function
    | 1 -> x * x
    | 2 -> (*) 10.0
    | 3 -> (+) 20.0
    | _ -> x

let fCreator = mkCreator 2

但是,mkCreator的主体在其所有的参数之前都不会被评估。这意味着,每次您随后申请fCreator时,您都会评估mkCreator的正文。但这正是我们试图避免的事情!

答案 3 :(得分:0)

非常感谢您的回复!

所以总体思路是在我的选择函数中使用匿名函数作为返回值。这非常好用,在上面的示例中是一种简单的方法。

但问题是(至少对我而言)在我的实际选择函数中,转换是通过大量递归函数调用创建的,这使得将它们定义为匿名函数并不容易。创建的转换(例如上面的x*x)实际上是操作和递归函数的序列,最好的办法就是让它们保持不变。

我希望只有通过更改调用选择函数的代码才能存储选定的转换。因此,我可以在Mark的示例中获得类似creator函数的内容,但保留selectCreator中的返回值,就像它们在我的原始示例中一样(不是作为匿名函数)。