I'm new to Haskell, I've to do a function that counts the number of vowels in a string using the higher order function foldr
I've tried to create this function
vowels [] = 0
vowels (x:xs)= if elem x "aeiou" then 1 + vowels xs else vowels xs
But it doesn't work and I'm not able to do it using foldr
, any suggestion?
Well a foldr :: (a -> b -> b) -> b -> [a] -> b
is a function where the first parameter is a function f :: a -> b -> b
. You can here see the a
parameter as the "head" of the list, the second parameter b
as the result of the recursion with foldr
, and you thus want to produce a result in terms of these two for the entire function. This logic is basically encapsulated in the second clause of your function.
Indeed:
vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs
can be rewritten as:
vowels (x:xs) = if elem x "aeiou" then 1 + rec else rec
where rec = vowels xs
and rec
is thus the outcome of the recursive call, the second parameter of the "fold"-function. x
on the other hand is the first parameter of the "fold"-function. We thus need to write this function, only in terms of x
and rec
, and this is simply:
\x rec -> if elem x "aeiou" then 1 + rec else rec
Furthermore we need to handle the case of an empty list, this is the first clause of your function. In that case the result is 0
, this is the second paramter of the foldr
, so we got:
vowels = foldr (\x rec -> if elem x "aeiou" then 1 + rec else rec) 0
Or a more clean syntax:
vowels = foldr f 0
where f x rec | elem x "aeiou" = 1 + rec
| otherwise = rec
We can further clean it up, by abstracting away rec
:
vowels = foldr f 0
where f x | elem x "aeiou" = (1+)
| otherwise = id
You need to take a look at foldr
's signature.
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Never mind the Foldable
part and focus on the first function it takes.
(a -> b -> b)
b
is the same type that you are supposed to return, so directly translating the signature into a lambda gives you \x acc -> acc
, but you want to do more than just ignore every element.
Take a look at your function if elem x "aeiou" then 1 + vowels xs else vowels xs
. You need to return b
, not recurse adding one to it.
if elem x "aeiou"
this part is fine. then 1 + acc
<- see what I'm doing here? I'm adding one to the accumulator, not recursing manually, that is done by foldr
, as for the else
case: acc
. That's it. You don't need to even touch x
.
Putting it all together: vowels = foldr (\x acc -> if elem x "aeiou" then 1 + acc else acc) 0
The 0
is what the acc
will start as.
If you want to know more about folds, I suggest you reimplement them yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With