【问题标题】:Currying in Haskell with 2+ arguments在 Haskell 中使用 2 个以上的参数进行柯里化
【发布时间】:2020-01-28 22:17:49
【问题描述】:

我开始学习 Haskell,所以我也需要了解柯里化(这也是我第一次看到这种技术)。我想我明白了它在某些情况下是如何工作的,其中 currification 只“消除”了其中一个参数。就像在下一个示例中,我试图计算 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 =

【问题讨论】:

  • 首先你需要意识到 Haskell 中的每个函数最多有一个参数。
  • @bipll 和 at least 一个参数。 IOW,每个函数都有恰好一个参数。
  • @leftaroundabout zero = 0
  • @bipll 这不是一个函数。 zero 的类型中没有 ->

标签: haskell currying pointfree tacit-programming


【解决方案1】:

这是非柯里化函数:

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 -&gt; a 作为第一个操作数执行操作,但该函数不是 Num 类型类的实例。

【讨论】:

  • 不过,这不是问题中所问的。
  • @WillNess:它要求创建一个柯里化函数,而不是一个无点函数。
  • 查看类型。 :) 这不是提问者第一次对术语感到困惑。
  • @WillNess:老实说,我看不出这些类型在这里做了什么 :) 特别是因为 Haskell 的语法已经被柯里化了,并且做一些无点的东西当然不应该改变类型。
  • prod'prod''的类型都是Integer-&gt;Integer-&gt;Integer-&gt;Integer-&gt;Integer。您的部分应用示例的类型不同。所以我将此解释为 OP 对术语的混淆。
【解决方案2】:

你拥有的是

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)

您所询问的是无点样式。 “无点”是指“没有明确提及[隐含的]论点”(“点”是数学家对“论点”的行话)。

“柯里化”是一个正交问题,尽管 Haskell 是一种柯里化的语言,使得这样的定义——以及威廉的回答中显示的部分应用程序——更容易编写。 “柯里化”意味着函数一次只接受一个参数,因此很容易将一个函数部分应用于一个值。

我们可以继续提取最后一个参数的过程out,这样它就可以通过 eta 减少进一步消除。但它通常会迅速导致越来越多的混淆代码,例如prod = ((((*) .) . (*)) .) . (*)

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

  prod =           
                  /
                 *
                / \
               *   
              / \
         <-- *   
              \

您可以尝试一下here。例如,如果(*) 是右关联的,我们会得到更复杂的代码

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

表示看起来很清晰,只是稍微重新排列的图形结构

              / 
         <-- *   
              \ /
               *
                \ /
                 *
                  \

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 1970-01-01
    • 2011-04-22
    • 1970-01-01
    • 2021-11-15
    相关资源
    最近更新 更多