功能实施"会员"使用" foldr"在哈斯克尔

时间:2015-10-12 20:18:32

标签: haskell member fold

我尝试过这样:

member e [] = False
member e xs = foldr (==) e xs

然后:

member 3 [1,2,3,4,5]

我收到此错误消息:

No instance for (Num Bool) arising from the literal `3'
In the first argument of `memb', namely `3'

我不知道这意味着什么......有人可以帮助我吗?

PS:member是一个函数,给定元素和元素列表,返回元素是否属于该列表。通常的实施是:

member a [] = False
member a (x:xs) | (a == x)  = True
                | otherwise = member a xs

PS2:完整代码

-- member:: Int -> [Int] -> Bool (Not necessary)
member e [] = False
member e xs = foldr (==) e xs

main = do
    putStrLn (show( member 3 [1,2,3] ) )

2 个答案:

答案 0 :(得分:7)

你几乎就在那里,但是你可以看到你的类型没有排队。如果你给member一个明确的类型签名本来会更好,我猜你想要像

这样的东西
member :: (Eq a) => a -> [a] -> Bool

这会让编译器抱怨当你试图使用它时,它不会进行类型检查而不是失败。问题是foldr (==)的类型为Bool -> [Bool] -> Bool,与您期望的不完全相同。

相反,你想要的更像是

member e xs
    = foldr (||)
            False
            (map (e ==) xs)

我已经将参数拆分为不同的行,以便更容易看到foldr的参数究竟是什么。这里的主要想法是将值列表转换为Bool列表,然后使用或运算符(||)将列表缩减为单个Bool。由于Haskell默认是惰性的,map (e ==) xs实际上不会计算任何内容,直到foldr需要元素,因此您不必将列表中的每个元素都与{{1}进行比较}。

你可以直接用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,直到找到xsacc的情况为止。这就是我之前选择True的原因,我只是将累加值的步骤与执行比较分开。

要记住map (e ==)(以及大多数其他折叠)的一件事是,第二个参数(也称为初始值)必须与foldr的最终结果具有相同的类型。在这种情况下,您希望foldr返回foldr,因此第二个参数也必须是Bool

解决这些问题的一个技巧,只要你有一个足够新的GHC版本,就是打字孔。这些允许你放入"洞"在表达式中,编译器会告诉你该洞应该是什么类型,所以如果你做了

Bool

GHC将打印出来

member :: Eq a => a -> [a] -> Bool
member e xs = foldr _1 _2 xs

这告诉您给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,因此您无法将其置于该洞中。这种技术可以帮助指导您定义函数,特别是当您不确定所有类型时,编译器会告诉您每个参数的用途。

答案 1 :(得分:0)

我们知道,使用伪代码语法,

foldr g z []            = z
foldr g z [a,b,c,...,n] = g a (foldr g z [b,c,...,n])

我们知道我们希望它成为

member x []            = False
member x [a,b,c,...,n] = (x==a) || member x   [b,c,...,n]
                       = foldr g z [a,b,c,...,n] 
                       = g   a     (foldr g z [b,c,...,n])
                                   ------------------------ r
                       = (x==a) || (foldr g z [b,c,...,n])
                       = foldr g z [a,b,c,...,n] 
                           where 
                               { z     = False ; 
                                 g a r = (x==a) || r }  --- r

因此使用我们定义的常规 Haskell 语法

member x  =  foldr g False  where  { g a r = (x==a) || r }