我目前正在开发一个项目,我必须根据一些规则创建很多函数,然后再重用这些函数。为此,我开始学习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#中做正确的方法。
我希望有人能在这里给我一个提示。
答案 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
中的返回值,就像它们在我的原始示例中一样(不是作为匿名函数)。