Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a high order function to capture common pattern haskell

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?

like image 441
realicado Avatar asked Apr 22 '18 13:04

realicado


People also ask

How do you define a higher order function in Haskell?

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 .

Is Foldr a higher order function?

The higher-order library function foldr (fold right) encapsulates this simple pattern of recursion, with the function ⊕ and the value v as arguments.

Why are higher order functions useful Haskell?

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.

What does A -> B mean in Haskell?

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 .


2 Answers

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.

like image 135
AJF Avatar answered Oct 07 '22 21:10

AJF


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.

like image 42
Johnny Liao Avatar answered Oct 07 '22 21:10

Johnny Liao