我开始学习Haskell,所以我也需要了解curring(这也是我第一次看到这种技术)。我想我知道在某些情况下,当前化仅“消除”其中一个参数是如何工作的。就像在下一个示例中,我试图计算4个数字的乘积。这是未固化的函数:
prod :: Integer->Integer->Integer->Integer->Integer
prod x y z t = x * y * z * t
这是咖喱函数:
prod' :: Integer->Integer->Integer->Integer->Integer
prod' x y z = (*) (x*y*z)
但是我不明白如何继续这种动态变化,例如仅使用两个参数执行相同的功能,依此类推:
prod'' :: Integer->Integer->Integer->Integer->Integer
prod'' x y =
这是未固化的功能:
prod :: Integer -> Integer -> Integer -> Integer -> Integer prod x y z t = x * y * z * t
这已已经咖喱函数。实际上,Haskell中的所有功能都是自动管理的。确实,您在这里编写了一个如下所示的函数:
prod :: Integer -> (Integer -> (Integer -> (Integer -> Integer)))
Haskell因此将产生一个看起来像这样的函数:
prod :: Integer -> (Integer -> (Integer -> (Integer -> Integer)))
prod = \x -> (\y -> (\z -> (\t -> x * y * z * t)))
事实上,我们可以生成这样的函数:
prod2 = prod 2
它将具有类型:
prod2 :: Integer -> (Integer -> (Integer -> Integer))
prod2 = prod 2
我们可以继续:
prod2_4 :: Integer -> (Integer -> Integer)
prod2_4 = prod2 4
并最终:
prod2_4_6 :: Integer -> Integer
prod2_4_6 = prod2_4 6
编辑
功能prod'
具有:
prod'' x y = (*) ((*) (x*y))
因为这意味着您将(*) (x*y)
与下一个参数相乘。但是(*) (x*y)
是一个功能。您只能将数字相乘。严格来说,您可以使函数编号。但是Haskell编译器因此抱怨:
Prelude> prod'' x y = (*) ((*) (x*y))
<interactive>:1:1: error:
• Non type-variable argument in the constraint: Num (a -> a)
(Use FlexibleContexts to permit this)
• When checking the inferred type
prod'' :: forall a.
(Num (a -> a), Num a) =>
a -> a -> (a -> a) -> a -> a
因此,您的目标是执行将函数a -> a
作为第一个操作数的操作,但该函数不是Num
类型类的实例。
你拥有的是
prod x y z t = x * y * z * t
= (x * y * z) * t
= (*) (x * y * z) t
因此通过eta减少(将foo x = bar x
替换为foo = bar
)
prod x y z = (*) (x * y * z)
= (*) ( (x * y) * z )
= (*) ( (*) (x * y) z )
= ((*) . (*) (x * y)) z
以便再次通过eta减少,
prod x y = (*) . (*) (x * y)
这里(.)
是函数组合运算符,定义为
(f . g) x = f (g x)
您要询问的内容称为无点样式。 “无点数”是指“没有明确提及[隐含的]自变量”(“点数”是数学家对“自变量”的用语)。
“ Currying”是一个正交的问题,尽管Haskell是咖喱的language使这样的定义和部分应用程序的定义(如Willem的回答所示)更容易编写。 “ Curing”表示函数一次只接受一个参数,因此很容易将一个函数部分地应用于一个值。
我们可以继续提取最后一个参数out的过程,因此可以通过进一步减少eta来消除它。但是它通常会迅速导致越来越混乱的代码,例如prod = ((((*) .) . (*)) .) . (*)
。
这是因为编写的代码是固有的二维(甚至更高维)计算图结构的一维编码,
prod =
/
*
/ \
*
/ \
<-- *
\
您可以尝试here。例如,如果(*)
是右关联的,我们将得到更复杂的代码
\x y z t -> x * (y * (z * t))
==
(. ((. (*)) . (.) . (*))) . (.) . (.) . (*)
表示外观清晰,只是稍微重新排列,图形结构
/
<-- *
\ /
*
\ /
*
\