我在短暂的中断之后再次学习Haskell,目前我正在努力更好地理解递归和lambda表达式在Haskell中的工作原理。
在这个:YouTube video中,有一个函数示例让我感到困惑的是它实际上应该如何工作:
firstThat :: (a -> Bool) -> a -> [a] -> a
firstThat f = foldr (\x acc -> if f x then x else acc)
为了清楚起见,因为对我来说并不是很明显,我将举例说明将这个函数应用于某些论点:
firstThat (>10) 2000 [10,20,30,40] --returns 20, but would return 2000, if none of the values in the list were greater than 10
如果我的假设是错误的,请纠正我。
似乎firstThat
有三个参数:
>
运算符实际上是一个中缀函数,上面示例中的第一个参数似乎是部分应用于>
函数的结果 - 这是正确的吗?但实际函数firstThat
似乎与其类型声明的定义不同,只有一个参数。由于foldr
通常需要我收集的三个参数,因此会发生某种部分应用。作为foldr
的参数提供的lambda表达式似乎也缺少其参数。
那么,这个功能究竟是如何运作的?如果我太过茂密或者没有看到森林里的树木,我道歉,但我无法绕过它,这令人沮丧。
非常感谢任何有用的解释或示例。
谢谢!
答案 0 :(得分:5)
一个接受一个参数并返回布尔值的函数。由于> operator实际上是一个中缀函数,上面例子中的第一个参数似乎是部分应用于>的结果。功能 - 这是正确的吗?
是的:(>10)
是\x -> x > 10
的缩写,正如(10>)
是\x -> 10 > x
的缩写。
与第一个参数
提供的函数的缺失参数相同类型的未指定值
首先,它不是一个缺失的参数:通过省略一个参数,你获得了一个函数值。但是,第二个参数的类型确实与函数>10
的参数匹配,就像它匹配列表[10,20,30,40]
的元素类型一样(这是更好的推理)。
上述类型的值列表
是
但实际的函数firstThat似乎与其类型声明的定义不同,只有一个参数。由于foldr通常采用我收集的三个参数,因此会发生某种部分应用。作为foldr的参数提供的lambda表达式似乎也缺少它的参数。
这是因为例如foo x y z = x * y * z
,这两行是等价的:
bar x = foo x
bar x y z = foo x y z
- 因为一个叫做currying的概念。 Currying也是函数类型签名不是(a, b) -> c
而是a -> b -> c
的原因,而a -> (b -> c)
由于->
类型运算符的正确关联性而相当于firstThat f = foldr (\x acc -> if f x then x else acc)
firstThat f x y = foldr (\x acc -> if f x then x else acc) x y
。
因此,这两行是等价的:
Data.List.find
注意:您还可以将Data.Maybe.fromMaybe
与λ> fromMaybe 2000 $ find (>10) [10, 20, 30]
20
λ> fromMaybe 2000 $ find (>10) [1, 2, 3]
2000
结合使用:
dialog.setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mI.onClick();
}
});
另见:
答案 1 :(得分:5)
但实际函数
firstThat
似乎与其类型声明的定义不同,只有一个参数。由于foldr
通常需要我收集的三个参数,因此会发生某种部分应用。
你是对的。然而,有一种更好的方式来表达它,而不是谈论“缺失的论点” - 一个不会引导你去问他们去哪里。以下是两种不缺少参数的方法。
首先,考虑这个功能:
add :: Num a => a -> a -> a
add x y = x + y
您可能知道,我们也可以这样定义:
add :: Num a => a -> a -> a
add = (+)
这是有效的,因为Haskell函数是与其他函数一样的值。我们可以简单地将值add
定义为等于另一个值(+)
,它恰好是一个函数。声明函数不需要特殊语法。结果是明确地写出论据(几乎)从来没有必要;这样做的主要原因是因为它经常使代码更具可读性(例如,我可以定义firstThat
而无需明确地编写f
参数,但我不这样做,因为结果相当可怕)。
其次,只要你看到一个带有三个参数的函数类型......
firstThat :: (a -> Bool) -> a -> [a] -> a
...你也可以这样读......
firstThat :: (a -> Bool) -> (a -> [a] -> a)
...也就是说,一个参数的函数产生两个参数的函数。这适用于多个参数的所有函数。关键的一点是,从本质上讲,所有Haskell函数只需要一个参数。这就是部分应用程序的原因。所以看到......
firstThat :: (a -> Bool) -> a -> [a] -> a
firstThat f = foldr (\x acc -> if f x then x else acc)
...您可以准确地说您已明确写出firstThat
所需的所有参数 - 即只有一个:)
作为
foldr
的参数提供的lambda表达式似乎也缺少其参数。
不是真的。 foldr
(限于列表时)是......
foldr :: (a -> b -> b) -> b -> [a] -> b
...所以传递给它的函数需要两个参数(根据上面的讨论,随意添加“两个”的空气引号)。 lambda写成......
\x acc -> if f x then x else acc
...有两个明确的参数,x
和acc
。