user8638529
user8638529

Reputation:

List of functions applying to argument

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

Answers (4)

Redu
Redu

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

NovaDenizen
NovaDenizen

Reputation: 5305

Just two cases, zero functions or at least one.

compose x [] = x
compose x (f:fs) = compose (f x) fs

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476493

General problem

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

Three or more elements

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

Zpalmtree
Zpalmtree

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

Related Questions