递增值的函数如何工作?

How does an a function that increments a value work?

经过多年的 OOP,我正在努力学习 haskell。我正在阅读快乐 Haskell。它提供了这个代码:

plus :: Int -> Int -> Int
plus x y = x + y

plus' :: Int -> Int -> Int
plus' = \x -> \y -> x + y

increment :: Int -> Int
increment = plus 1

increment' :: Int -> Int
increment' = (\x -> \y -> x + y) 1

我了解 plus 和 plus 的工作原理(它们是相同的,只是语法不同)。 但是增量,我不明白。

increment :: Int -> Int

意味着它需要一个整数,returns一个整数,对吧?但在那之后,实际功能是:

increment = plus 1

问题:

整数值增量在哪里? = 符号右侧不应该有一个 x 或其他东西,以表示该函数作为输入的整数值吗?类似于:

increment _ = plus 1 x

编辑:此外,增量的定义不应该是 Int -> (Int -> Int) 因为它需要一个 int 并将它传递给一个需要 int 和 [=34= 的函数] 和 int?

应该是increment x = plus 1 x,但一般来说foo x = bar xfoo = bar是一样的,因为如果f是一个函数returnsg(x) 每当使用任何参数 x 调用时,fg 的功能相同。所以 increment = plus 1 也一样有效。

这是因为 Haskell 中的所有函数都是隐式的 curried. As such, there is no distinction between a function which returns a function taking an argument and a function which takes two arguments returning a value (both have the type a -> a -> a). So calling plus (or any other function) with too few arguments simply returns a new function with the already-given arguments applied. In most languages, this would be an argument error. See also point-free style

Haskell 类型签名是右关联的,所以 a -> a -> a -> a 等价于 a -> (a -> (a -> a)).

部分申请

在Haskell中,您可以对函数进行柯里化和部分应用。看看 Haskell Wiki: Partial Application

特别是,如果你查看任何函数的类型签名,它的输入(参数)和输出之间没有真正的区别,这是因为你的函数 plus :: Int -> Int -> Int 实际上是一个函数,当给定一个 Int 时,将 return 另一个函数本身接受剩余的参数和 returns int:Int -> Int。这叫做部分应用

这意味着当您调用 increment = plus 1 时,您说的是增量等于 - 记住部分应用程序 - 一个函数(return 由 plus 1 编辑)本身取一个整数和 returns 一个整数。

由于Haskell是一种函数式编程语言,所有带等号的都不是赋值,而更像是一个定义,所以理解部分应用的一个简单方法就是遵循等号:

increment = plus 1 = 
            plus 1 y = 1 + y

主要用途

如您所见,部分应用可用于定义更具体的功能,例如将数字加 1 比仅将两个数字相加更具体。它还允许更多地使用无点样式,您可以在其中连接多个函数。

另请注意,使用中缀函数 lke (+),您可以部分应用于左侧或右侧,这对于非交换函数很有用,例如

divBy2 :: Float -> Float
divBy2 = (/2)

div2by :: Float -> Float
div2by = (2/)

Prelude> divBy2 3
1.5
Prelude> div2by 2
1.0

plusplus'的例子很有启发性。你看后者怎么好像没有参数,至少在等号左边是这样:

plus' :: Int -> Int -> Int
plus' = \x -> \y -> x + y

让我们制作另一对增量版本(我将以 "bumping" 数字命名它们——乘 1)到你给出的最终版本的一半:

bump :: Int -> Int
bump y = 1 + y

bump' :: Int -> Int
bump' = \y -> 1 + y

这两个定义之间的类比就像plusplus'之间的类比,所以这些应该是有道理的,包括后者,即使它的左边没有正式论证等号的一边。

现在,您对 bump' 的理解与您在问题中给出的对 increment' 的理解完全相同!事实上,我们将 bump' 定义为等于 increment' 等于的东西。

即(我们很快就会看到)bump' 定义的右侧,

\y -> 1 + y

等于

plus 1

这两个符号或表达式是定义 "the function that takes a number and returns one more than it."

的两种句法方式

但是是什么让它们相等?! 好吧,(正如其他回答者所解释的那样)表达式 plus 1 部分应用的 .在某种程度上,编译器知道 plus 需要两个参数(毕竟它是以这种方式声明的),所以当它出现在这里仅应用于一个参数时,编译器知道它仍在等待另一个参数。代表"waiting"给你一个函数,说你再给一个参数,不管是现在还是以后,都会让这个东西完全应用,程序实际上会跳转到[=14的函数体=](因此为给出的两个参数计算 x + y,表达式 plus 1 的文字 1 和稍后给出的 "one more" 参数)

Haskell 的乐趣和价值的一个关键部分是将功能视为 事物 本身,它们可以传递并非常灵活地从一个进入另一个。部分应用就是这样一种方式,将一个东西(当你想固定额外的值时带有 "too many arguments" 的函数)转换为 "just the right many." 的函数你可以将部分应用的函数传递给一个需要特定数量参数的接口。或者您可能只是想根据一个通用定义定义多个专用函数(因为我们可以定义通用 plus 和更具体的函数,如 plus 1plus 7)。