我最近开始学习F#并遇到以下简单示例的curried函数:
考虑一个通过将价格p
乘以销售单位数n
来计算销售额的函数。
let sales (p,n) = p * (float n);;
此功能的类型为
val sales : p:float * n:int -> float
即。取一对float
和int
并返回float
。
我们可以把它写成一个curried函数
let salesHi p n = p * (float n);;
此功能的类型为
val salesHi : p:float -> n:int -> float
即。获取float
并将int
的函数返回给float
。
在简单的情况下,这似乎没有区别
sales (0.99, 100);;
salesHi 0.99 100;;
两者都给予
val it : float = 99.0
然而,通过咖喱功能,我可以提供特定物品的价格以获得新功能。 E.g。
let salesBeer = salesHi 5.99;;
let salesWine = salesHi 14.99;;
然后salesBeer 2
提供11.98
,salesWine 2
提供29.98
。
另外,我注意到+
之类的内置运算符被定义为函数,所以我可以编写,例如:
let plus2 = (+) 2;
List.map plus2 [1;3;-1];;
并获取
val it : int list = [3; 5; 1]
这似乎是好事。因此,当我想在一个必须采用n > 1
参数的命令式语言中实现一个函数时,我是否应该总是在F#中使用一个curried函数(只要参数是独立的)?或者我应该采用简单的路线并使用常规功能n
- 元组和咖喱稍后如果有必要?或其他什么?
F#程序员如何决定何时以curry形式创建函数或使用带元组的常规函数?
答案 0 :(得分:31)
当你在curried和tupled形式之间做出选择时,要考虑的主要问题是你作为参数的元组是否意味着。
Tupled form。例如,float * float
可能代表一个范围,然后使用tupled表单是个好主意。
let normalizeRange (lo, hi) = if hi < lo then (hi, lo) else (lo, hi)
let expandRange by (lo, hi) = (lo - by, hi + by)
关于这一点的好处是你可以编写适用于范围的函数。例如,您可以编写类似的内容:
randomRange() |> normalizeRange |> expandRange 10
Curried form。另一方面,如果所有参数的元组不是具有某些有用含义的独立值,则curried形式是更好的选择。例如,幂函数pown 2.0 10
- 两个参数是幂和指数,但你不可能在你的程序中的其他地方使用元组(2.0, 10)
。
当你有一个“更重要”的参数时,curried表格也很有用,因为那时你可以使用流水线技术。例如,必须使用List.map
来实现此目的:
[1 .. 10] |> List.map (fun n -> n + 1)
答案 1 :(得分:3)
或者我应该采用简单的路线,并在必要时使用带有n元组和咖喱的常规功能?
你觉得做更多工作更简单了吗?让东西x y z = ...不仅仅是输入比让东西(x,y,z)更少,它实际上更少的语言工作。第二个版本必须分配一个元组,然后将元组解构为参数,而第一个版本只消耗参数。
curry表单是在F#中编写函数的惯用方法。除非数据已经存储为元组,否则我真的想不到使用元组形式的充分理由。
答案 2 :(得分:3)
另一个考虑因素 - 如果您计划与C#或VB .NET进行互操作,请不要理会咖喱表单,因为它们不能很好地暴露于这些语言。另一方面,从C#/ VB .NET的角度来看,tupled表单作为一组正常的参数公开,并且非常自然地使用。
答案 3 :(得分:2)
元组形式实际上有点危险。它可能看起来类似于ALGOL风格的语言(= 90%的流行语言) - 但它的工作方式不同。
考虑一下:
foo bar(x,y)
在所有允许使用此语法的ALGOL样式语言中,这意味着“以bar
和x
作为参数调用y
,并将结果传递给foo
” - 当foo
不必是一个方法时(它可以是语法,如2. * Python中的print
。)
但是,在Lambda风格的语言中(如ML,Haskell和F#),这意味着“以foo
作为参数调用bar
,然后调用结果(这是一个函数)元组(x,y)
作为参数。
如果您不熟悉Lambda风格的语言,这可能会令人困惑。现在,如果您使用的是咖喱形式,则相当于:
foo bar x y
这与foo bar(x,y)
一样错误 - 但不会令人困惑!即使不熟悉Lambda风格语言的程序员也很容易发现bar
不是foo bar x y
中要调用的第一个函数。错误很明显。