So i'm not too sure how to phrase this properly, but say I wanted to get the sum of all odd numbers in a list, do I have two functions (sumList and getOddNumbers) and combine them into sumOddList or is there a way to put these two together in a single function? If there isnt a better function, how exactly would I combine them into sumOddList?
getOddNumbers :: [Integer] -> [Integer]
getOddNumbers [] = []
getOddNumbers (x:xs)
|odd x = x:getOddNumbers xs
|otherwise = getOddNumbers xs
sumList :: [Integer] -> Integer
sumList list = case list of
[] -> 0
(x:xs) -> x + (sumList xs)
I also ask mainly because putting two diff functions together is something I struggled with before, when putting a colour and a shape using CodeWorld to output a shape of that colour.
Thank you
(Note: I've been using Haskell for just over 5 weeks now and I'm a total noob clearly)
Passing output as input to (another) function
Well what you basically want to do is use the output of the getOddNumbers
as input for the sumList
function, so we can define a sumOddList
function as:
sumOddList :: [Integer] -> Integer
sumOddList l = sumList (getOddNumbers l)
Here l
is the list we want to process, and the result is thus a function application on the result of getOddNumbers l
(with sumList
the function).
Chaining functions: the (.)
function
The above pattern is quite common: frequently we want to pass data first through a function g
, and the result through a function f
. Haskell has the (.) :: (b -> c) -> (a -> b) -> a -> c
function to "chain" functions. We can thus chain sumList
and getOddNumbers
together like:
sumOddList :: [Integer] -> Integer
sumOddList = (.) sumList getOddNumbers
Notice that we no longer use an l
parameter here. sumOddList
is here defined as a "pipeline" where data is first passed to the getOddNumbers
, and then is "post-processed" by the sumList
function.
The (.)
function can also be used as an infix operator:
sumOddList :: [Integer] -> Integer
sumOddList = sumList . getOddNumbers
In the following are three equivalent ways to write the function oddSum :: [Integer] -> Integer
:
oddSum xs = sumList (getOddNumbers xs)
oddSum xs = sumList $ getOddNumbers xs
oddSum = sumList . getOddNumbers
Btw, have a look at the filter
and sum
functions in the Prelude with which you could replace getOddNumbers
and sumList
respectively.
or is there a way to put these two together in a single function ... sumOddList
?
Yes there is.
Chaining functions by using one's output as the other's input works, under lazy evaluation especially, but leaves us reliant on the fusion to be performed by a compiler. Which after all is not guaranteed to happen (and often doesn't).
Instead, what you said :
mapping f cons x xs = cons (f x) xs
filtering p cons x xs = if (p x) then (cons x xs) else xs
transduce xf cons z xs = foldr (xf cons) z xs
sumOddList xs = transduce (filtering odd) (+) 0 xs
Thus,
> sumOddList [1..10]
25
> sum [1,3..10]
25
> transduce (mapping (+1) . filtering odd) (+) 0 [1..10]
35
> sum . filter odd . map (+1) $ [1..10]
35
> sum . map (+1) . filter odd $ [1..10]
30
> transduce (filtering odd . mapping (+1)) (+) 0 [1..10]
30
This works because folds fuse by composing their reducer functions' transformers (like the mapping
and the filtering
above which are transforming their reducer argument cons
):
foldr (+) 0
. foldr (\x r -> x+1 : r) []
. foldr (\x r -> if odd x then x : r else r) []
$ [1..10]
=
foldr (+) 0
. foldr ((\cons x r -> cons (x+1) r) (:)) []
. foldr ((\cons x r -> if odd x then cons x r else r) (:)) []
$ [1..10]
=
foldr ((\cons x r -> cons (x+1) r) (+)) 0
. foldr ((\cons x r -> if odd x then cons x r else r) (:)) []
$ [1..10]
=
foldr ((\cons x r -> if odd x then cons x r else r)
((\cons x r -> cons (x+1) r) (+))) 0
$ [1..10]
=
foldr ( ( (\cons x r -> if odd x then cons x r else r)
. (\cons x r -> cons (x+1) r) ) (+)) 0
$ [1..10]
=
foldr ( (filtering odd . mapping (+1)) (+)) 0
$ [1..10]
=
foldr ( filtering odd ( mapping (+1) (+))) 0
$ [1..10]
=
30
One foldr
doing the work of the three. Fusion explicitly achieved, by composing the reducer functions after the cons
operation has been abstracted from them, each such changed function becoming a cons
transformer, thus, liable to be composed with other such cons
-transforming functions.