用 2+ 参数在哈斯克尔中咒龋



我开始学习Haskell,所以我也需要了解咖喱(这也是我第一次看到这种技术(。我想我明白它在某些情况下是如何工作的,在这种情况下,curization只"消除"了其中一个参数。就像在下一个示例中,我尝试计算 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 = bar替换foo x = bar x(

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是一种柯里语言使得这样的定义 - 以及威廉回答中显示的部分应用定义 - 更容易写。"Currying"意味着函数一次获取一个参数,因此很容易将函数部分应用于值。

我们可以继续拉最后一个参数的过程,以便可以通过进一步减少 eta 来消除它。但它通常会很快导致越来越多的混淆代码,例如prod = ((((*) .) . (*)) .) . (*)

这是因为编写的代码是固有的二维(甚至更高维度(计算图结构的一维编码,

prod =           
/
*
/ 
*   
/ 
<-- *   

你可以在这里做实验。 例如,如果(*)是右关联,我们会得到更复杂的代码。

x y z t -> x * (y * (z * t))
==
(. ((. (*)) . (.) . (*))) . (.) . (.) . (*)

表示同样清晰,只是略微重新排列的图形结构

/ 
<-- *   
 /
*
 /
*

相关内容

  • 没有找到相关文章

最新更新