I'm currently trying to solve the following exercise:
Given a list of Int
s, count the number of times, an element is greater than the element that comes after it. The exercise forces me not to use explicit recursions.
Here are some example outputs given function :: [Int] -> Int
:
function [1, 2, 3, 4, 5] == 0 -- only increasing numbers
function [5, 4, 3, 2, 1] == 4 -- only decreasing numbers
function [2, 1, 3, 1, 0, 4] == 3
-- 2 > 1
-- 3 > 1
-- 1 > 0
function [1] == 0 -- no successor
function [ ] == 0 -- no numbers at all
I imagined to use in some way foldl
but after many attempts and not working idea I had to give up.
How can I count the number of times an element is greater than its successor without using recursion?
First we need to pair up the consecutive elements,
foo :: [Int] -> Int
foo xs = result
where
pairs = zip xs (drop 1 xs)
then we can process each pair
biggers = [ () | (x,y) <- pairs, x > y]
and now we can count them,
result = .......
All the nested names belong to the same, shared, nested scope. result
must make use of the value of biggers
, and biggers
refers to the value of pairs
which refers to the value of foo
's parameter, xs
. Make sure to put these code lines into the same definition, all indented by the same amount as the first one, for pairs
, one under the other.
Actually using a left fold is also possible:
foo (h:t) = snd ( foldl' (\ (a, !c) x -> (x, if (a > x) then (c+1) else c))
(h,0) t )
foo [] = 0
I think you'll agree though that this is much less self-apparent than the first definition. Also note that it uses a "bang pattern", !
, together with foldl'
, not foldl
, to do the counting as soon as possible as we go along the input list, not delaying it until all the input list is traversed in full as foldl
would do, needlessly, harming the overall efficiency.