8

我试图通过编写一些使用它们的简单函数来理解 Haskell 中的lambda 函数(即匿名函数)。

在下面的示例中,我只是尝试接收 3 个参数并使用匿名函数添加三个参数中的两个,然后添加第三个参数。我收到一条错误消息,说我必须先声明一个实例。

specialAdd x y z = (\x y -> x + y) + z

我感谢任何解释为什么我的示例不起作用和/或任何有助于我更好地理解 lambda 函数的解释。

4

5 回答 5

17

specialAdd x y z = (\x y -> x + y) + z

在此示例中,您尝试做的是向数字添加一个函数,但这是行不通的。看(\x y -> x + y) + z:它有形式a + b。为了使这样的表达式起作用,a零件和b零件必须是相同类型的数字。

Haskell 是一种不寻常的语言,所以它的错误消息很少是“你不能那样做”的形式。所以这里发生的事情是,Haskell 认为那(\x y -> x + y)是一个函数,并且由于在表达式中a + b,b必须与 的类型相同a,因此它得出结论 也b必须是一个函数。Haskell 还允许您定义自己的规则来添加非内置类型;所以它不能只给你一个错误说“你不能添加两个函数”,而是“你没有定义一个允许我添加两个函数的规则”。

以下将做你想要的:

specialAdd x y z = ((\x y -> x + y) x y) + z

在这里,您将函数(\x y -> x + y)应用于参数xy,然后将结果添加到z.

于 2013-03-27T15:34:57.370 回答
10

练习匿名函数的一个好方法是将它们与高阶函数一起使用,如折叠或映射。

使用地图作为入口点,

地图的基本定义,

map f [] = []
map f (x:xs) = f x : f xs  

建立了一个例子,

>>> let list = [0..4]
>>> let f x = x + 1

应用我们获得的地图,

>>> map f list 
[1,2,3,4,5]

现在,我们可以省略 f 的声明并使用匿名函数替换它,

>>> map (\x->x+1) list 
[1,2,3,4,5]

然后我们推断, map f list == map (\x->x+1) list,因此

f = \x-> x + 1 --- The same as f x = x + 1, but this is the equivalent lambda notation.  

然后从一个简单的函数开始,我们将了解如何将其转换为匿名函数,然后了解匿名函数如何依赖于 lambda 抽象。

作为练习,尝试转换 fx = 2*x。

现在更复杂了,一个接受两个参数的匿名函数,

又是一个工作示例,

>>> let add x y = x + y
>>> foldl' add 0 [0..4]
10

可以使用匿名函数重写为,

>>> foldl' (\x y -> x + y) 0 [0..4]  

再次使用相等我们推导出 add = \xy -> x + y
此外,在 hakell 中,所有函数都是一个参数的函数,我们可以部分应用它,我们可以将之前的匿名函数重写为, add = \x -> ( \y -> x + y)。

那么诀窍在哪里??因为,我只是将匿名函数的使用展示为高阶函数,并从那开始,展示如何利用它来使用 lambda 表示法重写函数。我的意思是它如何帮助你学习如何写下匿名函数?

仅仅是因为我已经给你(展示给你)一个使用高阶函数的现有框架。
这个框架是一个巨大的机会来适应你这个符号。
从中可以推断出无限范围的运动,例如尝试执行以下操作。

A - Find the corresponding anonymous function ?

1 - let f (x,y) = x + y in map f [(0,1),(2,3),(-1,1)]  
2 - let f x y = x * y in foldl' f 1 [1..5] 

B - Rewrite all of them using lambda notation into a declarative form (f = \x-> (\y-> ...) 

等等 ....


总结一下,

一个函数作为

(F0)   f x1 x2 ... xn = {BODY of f}

总是可以重写为,

(F1)   f = \x1 x2 ... xn -> {BODY of f}

在哪里

(F2)   (\x1 x2 ... xn -> {BODY of f})

F2 形式只是匿名函数,将函数纯粹转换为 lambda 演算形式。F1 是一个声明性的 lambda 表示法(因为我们声明了 f,正如我们定义的那样,将它绑定到匿名 F2)。F0 是 Haskeller 的常用符号。

最后一点重点是我们可以在参数之间加上括号,这会创建一个闭包。这样做意味着可以使用函数参数的子集完全评估函数代码的子集(意味着转换为不再出现自由变量的形式),但这是另一回事。

于 2013-03-27T14:31:05.723 回答
3

这是正确的形式:
specialAdd a b c = ((\x y -> x + y) a b) + c

来自Learn You a Haskell 的示例...:
zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]

很好的解释: http: //learnyouahaskell.com/higher-order-functions#lambdas

于 2013-03-27T13:41:46.303 回答
2

据我了解,Labmbda/Anonymous 函数可帮助您声明“内联”函数,而无需为其命名。对于“->”之后的表达式,“\”(希腊语的 ASCII,λ)位于变量名之前。例如,

(\x y -> x + y) 

是类似于 (+) 的匿名 (lambda) 函数。它接受两个 Num 类型的参数并返回它们的总和:

Prelude> :type (+)
(+) :: Num a => a -> a -> a

Prelude> :type (\x y -> x + y)
(\x y -> x + y) :: Num a => a -> a -> a

您的示例不起作用,因为正如其他人指出的那样,它的右侧使用 lambda 函数 (\xy -> x + y) 作为 (+) 运算符的参数,该运算符是默认定义的仅适用于 Num 类型的参数。lambda 函数的一些美妙之处在于它的“匿名”使用。Vladimir 展示了如何通过从左侧传递变量来在声明中使用 lambda 函数。更“匿名”的用法可能是简单地使用变量调用它,而不给函数命名(因此是匿名的)。例如,

Prelude> (\x y z -> x + y + z) 1 2 3
6

and if you like writing parentheses:

Prelude> (((+).) . (+)) 1 2 3
6

或者在更长的表达式中(如在您的示例声明中),例如,

Prelude> filter (\x -> length x < 3) [[1],[1,2],[1,2,3]]
[[1],[1,2]]
于 2013-03-28T04:21:10.533 回答
1

您正试图将其用作不正确(+)的东西。(Num a) => (a -> a -> a) -> a -> ??

(+)在类中定义Num并且 (a -> a -> a) 不是该类的实例。

你到底想达到什么目的?

于 2013-03-27T13:42:29.367 回答