f1 [] = 1
f1 (x:xs) = x * f1 xs
f2 [] = 0
f2 (x:xs) = 1 + f2 xs
f3 [] = 0
f3 (x:xs) = x + f3 xs
f4 [] = []
f4 (x:xs) = x ++ f4 xs
These all have a common behavior, how exactly can I identify the pattern and write a high order function to capture it?
As a Haskell definition it is. f x = x^2 f = \x -> x^2. which means that the function f is equivalent to the lambda expression \x -> x^2 . Consider the parameter of the higher-order function map , that is a function of type a -> b .
The higher-order library function foldr (fold right) encapsulates this simple pattern of recursion, with the function ⊕ and the value v as arguments.
In Haskell, a function that can take other functions as arguments or return functions is called a higher-order function. These are particularly useful in that they allow us to create new functions on top of the ones we already have, by passing functions as arguments to other functions.
a -> b Bool means... forall (a :: *) (b :: * -> *). a -> b Bool. b is therefore a type constructor taking a single type argument. Examples of single-argument type constructors abound: Maybe , [] , IO are all examples of things which you could use to instantiate b .
All* your functions have the form:
fn [] = P_0
fn (x:xs) = P_1 x (fn xs)
Where P_0
and P_1
are some constants. I'll call P_0
zero
, and P_1
combine
, and add them to the function:
-- P_0 P_1 list = result
fn zero _ [] = zero
fn zero combine (x:xs) = combine x (fn zero combine xs)
There we go. Now we have f1 = fn 1 (*)
, f3 = fn 0 (+)
, and f4 = fn [] (++)
. f2
is a little weird, but you can work it out: f2 = fn 0 (\_ b -> b+1)
.
Asking GHCi the type gives us that fn :: b -> (a -> b -> b) -> [a] -> b
, and Hoogling shows us that this function fn
is actually the function foldr
:
foldr :: (a -> b -> b) -> b -> [a] -> b
-- ^combine ^zero
So there you go: folds, or particularly right folds (the r
in foldr
means right) is the general pattern you're looking for.
By the way, there are also left folds. I'll leave you to try to work out what those might be. Hoogle could also help you here.
There's another pattern you could see here, called Monoids, but I'll also leave that to you, since it seems out of scope for this question.
* This may look weird for f2 (x:xs) = 1 + f2 xs
, because there isn't an x
involved in the result, but this is just the case where P_1 a b = 1 + b
, which is still technically the same form.
It looks like you can use foldr to represent all of them.
foldr (*) 1 [1,2,3] --f1
foldr (\_ a-> 1 + a) 0 [1,2,3] --f2
foldr (+) 0 [1,2,3] --f3
foldr (++) [] ["a","b","c"] --f4
They all need a list, an init value and an operator.
They all apply the operator from right to left: e.g f1 [1,2,3] = 1*2*(3*1)
So you can use foldr
to parameterize the operator and the init value.
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