3

我有函数 add,我部分应用它来创建一个新函数 addOne。

add :: Int -> (Int -> Int)
add x y = x + y

addOne 可以使用显式参数定义

addOne :: Int -> Int
addOne y = add 1 y

或没有显式参数

addOne :: Int -> Int
addOne = add 1

我有四个问题:

  1. 为什么我可以在没有显式参数的情况下定义新函数?
  2. 这两个定义有什么区别吗?
  3. 我什么时候知道什么时候可以定义没有参数的函数?
  4. 首选哪个定义以及何时?
4

2 回答 2

5
  1. 因为addOne y = add 1 y意味着addOne = \y -> add 1 y,而且\x -> f x永远是公正的f。这称为 eta 等价性。所以addOne = add 1

  2. 总是。函数参数只是 lambda 的语法糖:

    add :: Int -> Int -> Int
    add = \x y -> x + y
    

    是否可以完全删除变量绑定是另一回事。

  3. 尽可能“eta reduce”(即,当函数绑定与绑定表达式中的函数应用程序匹配时删除函数绑定中最右边的绑定变量)总是很好的,因为它避免了引入多余的名称。

于 2014-10-23T17:00:14.753 回答
4

您需要学习使用 Haskell 的函数式编程中的一个基本概念是,函数只是一种值,定义只是命名事物。它不像程序语言那样在函数变量之间有明显的区别,并且函数定义与变量定义完全不同。

所以像这样的变量定义

addOne :: Int -> Int
addOne = add 1

只是为表达式添加了一个名称add 1,因此您可以将其称为addOne. 它与变量声明相同。[1] 从 Haskell 的角度来看,该变量的值是一个函数这一事实几乎是偶然的。

你的add定义:

add :: Int -> (Int -> Int)
add x y = x + y

也是一个变量定义。这是 Haskell 提供的一些语法糖:

add :: Int -> Int -> Int
add = \ x -> \ y -> x + y

基于更容易阅读的理论。但它仍然只是糖;您永远不需要它(见下文 [1] 除外),就像使用其他语言一样。

[1]:可怕的单态性限制也在这里发挥作用。这个想法很简单:在函数定义中,RHS 将被计算机执行多次(与您调用函数一样多次)。您可能从其他语言中知道这一点。在单态变量定义中,RHS 最多会执行一次,这也类似于其他语言的工作方式。但是,多态变量通常最终会像函数定义一样执行,RHS 的执行次数与访问变量值的次数一样多。所以 Haskell 不允许多态定义,除非你有一个多态类型签名(所以你说“我知道我在做什么,允许这个变量是多态的”)或者你在左侧有参数(所以它“看起来像“RHS应该执行多次)。

于 2014-10-23T17:30:36.643 回答