2

经过多年的OOP,我正在尝试学习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

意味着它需要一个 int,并返回一个 int,对吗?但在那之后,实际的功能是:

increment = plus 1

问题:

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

increment _ = plus 1 x

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

4

4 回答 4

5

部分申请

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

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

这意味着当您调用时,increment = plus 1您是在说增量等于——记住部分应用程序——一个函数(由 返回plus 1),它本身接受一个整数并返回一个整数。

由于 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
于 2019-02-27T02:10:52.713 回答
4

它会是increment x = plus 1 x,但通常foo x = bar x是一样的,foo = bar因为 iff是一个g(x)在任何时候使用任何参数调用时都会返回的函数x, thenf是相同的函数g。所以increment = plus 1同样有效。

于 2019-02-27T01:57:03.827 回答
2

这是因为 Haskell 中的所有函数都是隐式柯里化的。因此,返回带参数的函数的函数和带两个参数返回值的函数之间没有区别(两者都具有a -> a -> a†</sup> 类型)。因此plus,使用太少的参数调用(或任何其他函数)只会返回一个应用了已给定参数的新函数。在大多数语言中,这将是一个参数错误。另请参阅无点样式

†</sup> Haskell 类型签名是右关联的,因此a -> a -> a -> a等价于a -> (a -> (a -> a)).

于 2019-02-27T02:05:09.513 回答
2

plus和的例子plus'具有指导意义。您会看到后者似乎没有参数,至少在等号的左侧:

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

让我们制作另一对增量版本(我将在“碰撞”一个数字之后命名它们 - 加 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

这两个符号或表达式是定义“接受一个数字并返回一个数字的函数”的两种语法方式。

但是是什么让他们平等?!好吧,(正如其他回答者所解释的那样)表达式plus 1部分应用的。在某种程度上,编译器知道这plus需要两个参数(毕竟它是这样声明的),所以当它出现在这里只应用于一个参数时,编译器知道它仍在等待另一个参数。它通过给你一个函数来表示“等待”,说,如果你再给一个参数,无论是现在还是以后,这样做都会使这个东西完全应用,程序实际上会跳转到函数体plus(因此x + y计算给出的两个参数,1表达式中的文字plus 1和稍后给出的“另一个”参数)

Haskell 的乐趣和价值的一个关键部分是将函数视为事物本身,可以传递并非常灵活地从一个转换为另一个。部分应用就是将一件事(当你想修复额外的值时,一个“参数太多”的函数)转换为一个“恰到好处”的函数的一种方式。您可以将部分应用的函数传递给需要特定数量参数的接口。或者您可能只是想基于一个通用定义定义多个专用函数(因为我们可以定义通用plus和更具体的函数,例如plus 1and plus 7)。

于 2019-02-27T05:57:19.113 回答