是否可以改善我的过滤器实现?

时间:2018-11-07 20:13:21

标签: haskell fold

Haskell的《第一原理》中的一项练习说,要使用filter来实现foldr,这是我想到的,但是感觉很笨拙。有没有更自然的方法来使用foldr来实现它?

import Data.Bool
myFilter :: (a -> Bool) -> [a] -> [a]
myFilter f = foldr (\x -> bool (++ []) ((:) x) (f x)) []

4 个答案:

答案 0 :(得分:5)

如果它使我简单地通过使用谓词bool组成对bool的调用来摆脱lambda表达式,那么我将只使用pbool 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)

在这种情况下,您的hfilter 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将其打开。

好吧,foldrfoldr (\x -> ([x | f x] ++)) []