Haskell的《第一原理》中的一项练习说,要使用filter
来实现foldr
,这是我想到的,但是感觉很笨拙。有没有更自然的方法来使用foldr
来实现它?
import Data.Bool
myFilter :: (a -> Bool) -> [a] -> [a]
myFilter f = foldr (\x -> bool (++ []) ((:) x) (f x)) []
答案 0 :(得分:5)
如果它使我简单地通过使用谓词bool
组成对bool
的调用来摆脱lambda表达式,那么我将只使用p
: bool iffalse iftrue . p
。但是,p
不是唯一需要在列表元素上调用的函数; (:)
也是如此。您可以使用Applicative
实例来编写函数
myfilter p = foldr (bool id . (:) <*> p) [] -- yuck
但是在这种情况下,我将只在lambda表达式内使用一个简单的if
表达式:
myfilter p = foldr (\x -> if p x then (x:) else id) [] -- much clearer!
请注意,当专门用于函数时,Applicative
的{{1}}运算符被定义为(<*>)
。我保留使用该定义将f <*> g = \x -> f x (g x)
转换为
bool id . (:) <*> p
。
答案 1 :(得分:2)
您可以使用Applicative
的{{1}}实例来使Lambda更加整洁。但是,如果您想使用(->) a
,我认为您可以进行任何实质性的更改:
foldr
myFilter f = foldr (bool id <$> (:) <*> f) []
的意思是bool id <$> (:) <*> f
。 \x -> bool id ((:) x) (f x)
的类型为bool id
。 ([a] -> [a]) -> Bool -> ([a] -> [a])
的类型为(:)
,而a -> [a] -> [a]
的类型为f
。当以这种方式使用a -> Bool
和(<$>)
时,您可以认为它假装(<*>)
和(:)
没有f
参数,使得它们分别a
和[a] -> [a]
,将它们应用于Bool
以获得bool id
,然后通过重新引入[a] -> [a]
参数结束谎言,使{ {1}}。操作员负责a
的线程,因此您不需要lambda抽象。
答案 2 :(得分:1)
它不仅会搜索更优雅的实现,还可以帮助您更多地了解搜索实现的优雅过程。这样应该可以更轻松地找到优雅的解决方案。
对于列表上的任何功能h
,我们都有
h = foldr f e
当且仅当
h [] = e
h (x:xs) = f x (h xs)
在这种情况下,您的h
是filter p
的某个布尔函数p
,用于选择要保留的元素。将filter p
实现为“简单”的递归函数并不难。
filter p [] = []
filter p (x:xs) = if p x then x : (filter p xs) else (filter p xs)
第一行表示e = []
。第二行需要以f x (filter p xs)
的形式编写,以匹配上面的h
的等式,以便我们推断出要插入f
的{{1}}。为此,我们仅对这两个表达式进行抽象。
foldr
所以我们发现
filter p [] = []
filter p (x:xs) = (\x ys -> if p x then x : ys else ys) x (filter p xs)
因此,
e = []
f x ys = if p x then x: ys else ys
要了解有关使用filter p = foldr (\y ys -> if p y then y : ys else ys) []
的这种方法的更多信息,建议阅读
"A tutorial on the universality and expressiveness of fold",作者:格雷厄姆·赫顿(Graham Hutton)。
一些补充说明:
万一这看起来过于复杂,请注意,尽管上述原则可以通过代数操纵以“半严格”方式使用,但也可以并且也应被用来指导您的直觉并帮助您进行非正式的开发。
foldr
的方程式使如何查找h (x:xs) = f x (h xs)
变得更加清晰。在f
是过滤功能的情况下,您需要一个h
,它将元素f
与已经被过滤的尾部结合在一起。如果您真的了解这一点,那么应该很容易得出结论,
x
答案 3 :(得分:0)
是的,有:
myFilter :: (a -> Bool) -> [a] -> [a]
myFilter f = foldMap (\x -> [x | f x])
> myFilter even [1..10]
[2,4,6,8,10]
看到,我用foldMap
将其打开。
好吧,foldr
是foldr (\x -> ([x | f x] ++)) []
。