这可能是最简单的一个例子.
假设我有一个相当冗长的函数,可以提升整数x到n次幂.
let powInt = function
| x,n ->
let rec loop acc n =
match n with
| 0 -> acc
| v -> loop (acc * x) (v-1)
loop 1 n
Run Code Online (Sandbox Code Playgroud)
如果我现在想将整数列表中的每个整数提升到n次幂,我认为F#中的酷List.map函数将是要走的路:
let powIntList (xs: int list) = List.map powInt xs
Run Code Online (Sandbox Code Playgroud)
然而,在最后一个片段中,n功率缺乏作为映射中的参数 - 而int x由函数隐式提取map.
我们如何在这个例子中添加n power参数?
你把你的功能搞定了.
这恰恰就是currying的情况,这是一种以这样一种方式来制定你的功能的技术,即它们可以"逐个"地获取参数.在数学上,这样的函数只接受一个参数,并返回另一个接受第二个参数的函数.像这样的东西:
let curriedPowInt = fun n -> fun x -> powInt (x, n)
Run Code Online (Sandbox Code Playgroud)
这种定义函数的方式很常见.事实上,它是ML语言(其中F#是一个)的本质的核心,它有一个特殊的语法:
let curriedPowInt n x = powInt (x, n)
Run Code Online (Sandbox Code Playgroud)
看看我如何在一行中列出我的参数,用空格分隔它们,比如n x =?这是语法糖fun n -> fun x ->.从逻辑上讲,它定义了一个"逐个"参数的函数,而不是一次作为元组.
既然你有这样的功能,你可以部分应用它 - 也就是说,只给它一个参数,而不是两个:
let pow2 = curriedPowInt 2
Run Code Online (Sandbox Code Playgroud)
这有效,因为 - 记得吗?- 我的函数curriedPowInt接受n并返回另一个函数x.该类型的pow2现在int -> int.这是一个功能,它需要int并将其提升到第二个功率.
当然,你也可以在线使用这个技巧:
let powIntList (xs: int list) = List.map (curriedPowInt n) xs
Run Code Online (Sandbox Code Playgroud)
另外,请注意我如何更改参数的顺序.我把n参数放在第一位,x参数放在最后.这是使您的函数更有用的一般规则:将"最重要"参数放在最后,将"最不重要"放在第一位.List.map例如,看看它自己:它运行的列表是两者的最后一个参数.
最后,我建议不要声明前者,而不是powInt单独curriedPowInt声明.只需将所有功能都用于咖喱.Curried函数更有用.事实上,我很少见到未经证实的函数会有用的场合,我可能也从未见过任何函数.
所以,总结一下:
let powInt n x =
let rec loop acc n =
match n with
| 0 -> acc
| v -> loop (acc * x) (v-1)
loop 1 n
let powIntList (xs: int list) = List.map (powInt n) xs
Run Code Online (Sandbox Code Playgroud)