你已经快到了,但正如你所看到的,你的类型没有对齐。如果你给予的话那就更好了member
一个显式的类型签名,我猜你想要类似的东西
member :: (Eq a) => a -> [a] -> Bool
这会让编译器抱怨这不会进行类型检查,而不是在您尝试使用它时失败。问题是foldr (==)
有类型Bool -> [Bool] -> Bool
,不完全符合您的预期。
相反,你想要的更像是
member e xs
= foldr (||)
False
(map (e ==) xs)
我在这里将参数分成不同的行,以便更容易查看参数的内容foldr
实际上是。这里的主要想法是将值列表转换为列表Bool
s,然后使用或运算符 (||
) 将列表缩减为单个Bool
。由于 Haskell 默认是惰性的,map (e ==) xs
在需要元素之前不会实际计算任何内容foldr
,因此您不必将列表中的每个元素与e
.
你可以直接实现这个foldr
使用更复杂的累加器(第一个参数foldr
):
member e xs = foldr (\x acc -> if acc then x else e == x) False xs
这可以等效地写为
member e xs = foldr (\x acc -> acc || e == x) False xs
注意在这种情况下我们必须如何执行e == x
对于每一个x
in xs
直到我们找到一个案例acc
is True
。这就是我选择的原因map (e ==)
早些时候,我只是将执行比较的步骤与累加值的步骤分开。
需要记住的一件事foldr
(和大多数其他折叠)是第二个参数,也称为初始值,必须具有与最终结果相同的类型foldr
。在这种情况下你想要foldr
返回一个Bool
,所以第二个参数必须是Bool
以及。
如果您有足够新的 GHC 版本,解决这些问题的一个技巧就是键入漏洞。这些允许您在表达式中放入“洞”,编译器会告诉您该洞应该是什么类型,所以如果您这样做
member :: Eq a => a -> [a] -> Bool
member e xs = foldr _1 _2 xs
GHC 将打印出来
Found hole `_1` with type: a -> Bool -> Bool
...
Relevant bindings include
xs :: [a]
e :: a
Found hole `_2` with type: Bool
...
Relevant bindings include
xs :: [a]
e :: a
这告诉您赋予的函数foldr
必须有类型a -> Bool -> Bool
并且初始值必须具有类型Bool
。自从e
有类型a
你不能把它按原样放进那个洞里。这种技术可以帮助指导您定义函数,特别是当您不确定所有类型时,编译器会告诉您每个参数使用什么。