我试图通过编写一些使用它们的简单函数来理解Haskell中的 lambda函数(即匿名函数)。
在下面的示例中,我只是尝试接受3个参数,并使用匿名函数添加三个参数中的两个,然后添加第三个参数。 我收到一个错误,说我必须先声明一个实例。
specialAdd x y z = (\x y -> x + y) + z
我感谢任何解释为什么我的例子不起作用和/或任何有助于我更好地理解lambda函数的解释。
答案 0 :(得分: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)
应用于参数x
和y
,然后将结果添加到z
。
答案 1 :(得分:10)
练习匿名函数的一个好方法是使用高阶函数作为折叠或贴图。
使用map作为入口点,
地图的基本定义,
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抽象。
作为练习尝试翻译f x = 2 * x。
现在更复杂,一个带有两个参数的匿名函数,
再一个工作的例子,
>>> let add x y = x + y
>>> foldl' add 0 [0..4]
10
可以使用匿名函数重写,
>>> foldl' (\x y -> x + y) 0 [0..4]
再次使用相等,我们推断出add = \ x y - > 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的常用符号。
最后一个注意事项,我们可以在参数之间放置括号,这会创建一个闭包。这样做意味着可以使用函数参数的子集完全评估函数代码的子集(意味着转换为不再发生自由变量的形式),但这是另一个故事。
答案 2 :(得分:3)
这是正确的形式:
specialAdd a b c = ((\x y -> x + y) a b) + c
向你学习哈斯克尔的例子......:
zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]
很好的解释: http://learnyouahaskell.com/higher-order-functions#lambdas
答案 3 :(得分:2)
根据我的理解,Labmbda / Anonymous函数可帮助您声明函数“inline”而无需为其命名。 “\”(希腊语的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]]
答案 4 :(得分:1)
您正试图将(+)
用作(Num a) => (a -> a -> a) -> a -> ??
之类的错误。
(+)
在课程Num
中定义,(a - &gt; a - &gt; a)不是此课程的实例。
你到底想要达到什么目的?