Reputation:
Task is to make function, taking list of unary functions (+1,*2 etc.) (3 at least) and consistently applying them to argument. For example: func 1 [(+1),(+1),(/2)] == 1.5. Here's what i made:
compose x [(y),(c),(z)] = map ($ x) [(z) . (c) . (y)]
But how can I make unlimited list of functions? In case if user wants more than 3 functions. Another problem is that this function returns list with one element instead of just one element ([4] instead of 4) and I'm kinda stuck with it
Upvotes: 4
Views: 1488
Reputation: 26161
This can simply be done by using foldr1
to cascade the list functions by flipped compose operators
Prelude> foldr1 (flip (.)) [(+5),(/2),(*3)] $ 1
9.0
Upvotes: 0
Reputation: 5305
Just two cases, zero functions or at least one.
compose x [] = x
compose x (f:fs) = compose (f x) fs
Upvotes: 0
Reputation: 476493
You can use foldl
for this: in that case you pass an accumulator left-to-right over the list, and perform some operation over it:
So your compose
then would look something like:
compose :: a -> [(a -> a)] -> a
compose x0 fs = foldl (??) x0 fs
So now the question is what we do in ??
. The first argument is the accumulator, the second an element of the list, a function. So we have to implement something like:
compose :: a -> [(a -> a)] -> a
compose x0 fs = foldl g x0 fs
where g xi f = ...
we thus want to apply f
to xi
to obtain the next xi
. Or in other words:
compose :: a -> [(a -> a)] -> a
compose x0 fs = foldl g x0 fs
where g xi f = f xi
If we aim to simplify the g
function, we can write it as:
compose :: a -> [(a -> a)] -> a
compose x0 fs = foldl g x0 fs
where g = flip ($)
and further improve it to:
compose :: a -> [(a -> a)] -> a
compose = foldl (flip ($))
This then produces:
Prelude> foldl (flip ($)) 1 [(+1)]
2
Prelude> foldl (flip ($)) 1 [(+1),(+1)]
3
Prelude> foldl (flip ($)) 1 [(+1),(+1),(/2)]
1.5
If you want to restrict the number of elements to three or more, we can use pattern matching:
compose :: a -> [(a -> a)] -> a
compose x0 fs@(_:_:_:_) = foldl (flip ($)) x0 fs
although it is a bit weird that the program is not supposed to work on lists with no, one or two elements.
Upvotes: 3
Reputation: 1359
This should do the trick:
compose x ys = foldl (\a b -> b a) x ys
This can be ETA reduced to be
compose = foldl (\a b -> b a)
or alternatively
compose x [] = x
compose x (y:ys) = compose (y x) ys
It applies the function to the input, then uses that input as the next input until it reaches the end of the list, and it then returns the final value.
λ> compose 1 [(+1), (+1), (/2)]
1.5
λ> compose 1 [(+1), (+1), (/2), (*10), (+(-1))]
14.0
λ> compose 1 []
1
λ>
Because of how haskell parses minuses, I think you have to make a minus function by adding a negative.
If the user has to enter at least 3 functions, you could do this:
compose x ys
| length ys < 3 = error "Must enter at least 3 functions!"
| otherwise = foldl (\a b -> b a) x ys
Alternatively, you could return a Maybe, as it's not very good style to error on something recoverable.
compose x ys
| length ys < 3 = Nothing
| otherwise = Just $ foldl (\a b -> b a) x ys
Upvotes: 3