标准 ML 中的 Curried 函数
Curried Functions in Standard ML
我一直在用头撞墙试图了解柯里化函数。到目前为止,这是我的理解;假设我有一个函数:
fun curry (a b c) = a * b * c;
或
fun curry a b c = a * b * c;
在 ML 中,我只能有一个参数,因此第一个函数使用 3 元组来解决这个问题/访问 a
、b
和 c
。
在第二个例子中,我真正拥有的是:
fun ((curry a) b) c
其中 curry a
returns 一个函数,并且
(curry a) b
returns 一个函数和 ((curry a) b) c
returns 另一个函数。几个问题:
1) 为什么这比使用元组更可取?难道只是我可以利用中间函数curry a
和(curry a) b
。我的书提到了部分实例化,但不是很清楚。
2) 您如何确定 curry a
、(curry a) b
实际执行的功能? ((curry a) b)
c 就是 a * b * c
,对吧?
感谢您帮助解决这个问题,
克莱曼
使用柯里化函数与非柯里化函数是有品味的。我当然不会使用柯里化函数。例如,如果要编写一个 gcd 函数,我倾向于将其编写为一个旨在对元组进行操作的函数,因为我很少使用已定义的部分实例化的 gcd 函数。
柯里化函数真正有用的地方在于定义高阶函数。考虑 map
。编写非柯里化版本很容易:
fun mymap (f,[])= []
| mymap (f,x::xs) = f(x)::mymap(f,xs)
它的类型 fn : ('a -> 'b) * 'a list -> 'b list
接受一个元组,该元组由两个类型之间的函数和输入类型的元素列表组成,返回输出类型的元素列表。这个函数没有什么 错误 ,但是 -- 它与 SML 的 map
不同。内置地图的类型为
fn : ('a -> 'b) -> 'a list -> 'b list
咖喱。 curried 函数为我们做了什么?一方面,它可以被认为是一个函数转换器。您为地图提供一个函数 f
,旨在对给定类型的元素进行操作,它 returns 作为函数 map f
,旨在对整个元素列表进行操作。例如,如果
fun square(x) = x*x;
是一个函数,旨在对 ints
进行平方,然后 val list_square = map square
将 list_square
定义为一个接受元素列表和 returns 其平方列表的函数。
当你在像 map square [1,2,3]
这样的调用中使用 map
时,你必须记住函数应用程序是关联的,因此它被解析为
'(地图广场) [1,2,3]. The function
地图广场*is* the same as the function
list_squareI defined above. The invocation
地图广场[1,2,3]takes that function and applies it to
[1,2,3]yielding
[1,4,9]`.
如果您想定义一个函数 metamap
,柯里化版本非常好,它可用于将函数应用于被认为是列表列表的矩阵的每个元素。使用 curried 版本很简单:
fun metamap f = map (map f)
像(在 REPL 中)使用:
- metamap square [[1,2],[3,4]];
val it = [[1,4],[9,16]] : int list list
逻辑是map
提升一个函数从应用于元素到应用于列表。如果你想让一个函数应用于列表的列表(例如矩阵),只需应用 map
两次——这就是 metamap
所做的全部。当然,您 可以 使用我们的非柯里化 mymap
函数编写一个 metamap
的非柯里化版本(它甚至不会那么难) ,但您将无法接近上面 1 行定义的优雅。
我一直在用头撞墙试图了解柯里化函数。到目前为止,这是我的理解;假设我有一个函数:
fun curry (a b c) = a * b * c;
或
fun curry a b c = a * b * c;
在 ML 中,我只能有一个参数,因此第一个函数使用 3 元组来解决这个问题/访问 a
、b
和 c
。
在第二个例子中,我真正拥有的是:
fun ((curry a) b) c
其中 curry a
returns 一个函数,并且
(curry a) b
returns 一个函数和 ((curry a) b) c
returns 另一个函数。几个问题:
1) 为什么这比使用元组更可取?难道只是我可以利用中间函数curry a
和(curry a) b
。我的书提到了部分实例化,但不是很清楚。
2) 您如何确定 curry a
、(curry a) b
实际执行的功能? ((curry a) b)
c 就是 a * b * c
,对吧?
感谢您帮助解决这个问题,
克莱曼
使用柯里化函数与非柯里化函数是有品味的。我当然不会使用柯里化函数。例如,如果要编写一个 gcd 函数,我倾向于将其编写为一个旨在对元组进行操作的函数,因为我很少使用已定义的部分实例化的 gcd 函数。
柯里化函数真正有用的地方在于定义高阶函数。考虑 map
。编写非柯里化版本很容易:
fun mymap (f,[])= []
| mymap (f,x::xs) = f(x)::mymap(f,xs)
它的类型 fn : ('a -> 'b) * 'a list -> 'b list
接受一个元组,该元组由两个类型之间的函数和输入类型的元素列表组成,返回输出类型的元素列表。这个函数没有什么 错误 ,但是 -- 它与 SML 的 map
不同。内置地图的类型为
fn : ('a -> 'b) -> 'a list -> 'b list
咖喱。 curried 函数为我们做了什么?一方面,它可以被认为是一个函数转换器。您为地图提供一个函数 f
,旨在对给定类型的元素进行操作,它 returns 作为函数 map f
,旨在对整个元素列表进行操作。例如,如果
fun square(x) = x*x;
是一个函数,旨在对 ints
进行平方,然后 val list_square = map square
将 list_square
定义为一个接受元素列表和 returns 其平方列表的函数。
当你在像 map square [1,2,3]
这样的调用中使用 map
时,你必须记住函数应用程序是关联的,因此它被解析为
'(地图广场) [1,2,3]. The function
地图广场*is* the same as the function
list_squareI defined above. The invocation
地图广场[1,2,3]takes that function and applies it to
[1,2,3]yielding
[1,4,9]`.
如果您想定义一个函数 metamap
,柯里化版本非常好,它可用于将函数应用于被认为是列表列表的矩阵的每个元素。使用 curried 版本很简单:
fun metamap f = map (map f)
像(在 REPL 中)使用:
- metamap square [[1,2],[3,4]];
val it = [[1,4],[9,16]] : int list list
逻辑是map
提升一个函数从应用于元素到应用于列表。如果你想让一个函数应用于列表的列表(例如矩阵),只需应用 map
两次——这就是 metamap
所做的全部。当然,您 可以 使用我们的非柯里化 mymap
函数编写一个 metamap
的非柯里化版本(它甚至不会那么难) ,但您将无法接近上面 1 行定义的优雅。