Implementation of function “member” using “foldr” in Haskell

喜你入骨 提交于 2021-02-16 14:02:44

问题


I tried like that:

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

and then:

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

and I get this error message:

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

I don't know what it means... can someone help me?

PS: member is a function that, given a element and a list of elements, returns whether the element belongs to that list or not. The usual implementation is:

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

PS2: full code

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

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

回答1:


You're almost there, but as you can see your types don't line up. It would have been better if you had given member an explicit type signature, I'm guessing that you want something like

member :: (Eq a) => a -> [a] -> Bool

This would have made the compiler complain that this doesn't type check instead of failing when you tried to use it. The problem is that foldr (==) has the type Bool -> [Bool] -> Bool, not quite what you were expecting.

Instead, what you want is something more like

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

I've split up the arguments onto separate lines here so that it's easier to see what the arguments to foldr actually are. The big idea here is to convert the list of values into a list of Bools, then use the or operator (||) to reduce the list into a single Bool. Since Haskell is lazy by default, map (e ==) xs won't actually compute anything until elements are needed by foldr, so you won't necessarily compare every element in the list to e.

You could implement this directly with only foldr with a more complicated accumulator (the first argument to foldr):

member e xs = foldr (\x acc -> if acc then x else e == x) False xs

Which can equivalently be written as

member e xs = foldr (\x acc -> acc || e == x) False xs

Notice how in this case we have to perform e == x for every x in xs until we find a case where acc is True. This is why I chose map (e ==) earlier, I just separated the step of performing the comparison from the step of accumulating the values.

One thing to keep in mind with foldr (and most other folds) is that the second argument, also called the initial value, has to have the same type as final result of foldr. In this case you're wanting foldr to return a Bool, so the second argument must be a Bool as well.

A trick for working through these problems, provided you have a new enough version of GHC, is typed holes. These allow you to put in "holes" in an expression and the compiler will tell you what type that hole should be, so if you do

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

GHC will print out

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

This tells you that the function given to foldr must have the type a -> Bool -> Bool and the initial value must have type Bool. Since e has type a you can't put it as is in that hole. This technique can help guide you through defining your functions, particularly when you aren't sure of all the types, with the compiler telling you what to use for each argument.




回答2:


We know that, with a pseudocode syntax,

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

and we know that we want it to be

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

and so with the regular Haskell syntax we define

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


来源:https://stackoverflow.com/questions/33089505/implementation-of-function-member-using-foldr-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!