Let’s look at a sample evaluation of evens
on an input list. I’ll use "abcde"
—recall that String
is an alias for a list of characters [Char]
, so this is equivalent to ['a', 'b', 'c', 'd', 'e']
or 'a' : 'b' : 'c' : 'd' : 'e' : []
.
We start with the initial input:
evens "abcde"
Match the first pattern of evens
, adding 'a'
to the beginning of the result and proceeding on the remainder of the list:
evens "abcde"
-------------
-- evens (x : xs) = x : odds xs
-- x = 'a'
-- xs = "bcde"
-- evens "abcde" = 'a' : odds "bcde"
'a' : odds "bcde"
-----------------
Match the first pattern of odds
, ignoring 'b'
and proceeding:
'a' : odds "bcde"
-----------
-- odds (_ : xs) = evens xs
-- xs = "cde"
-- odds "bcde" = evens "cde"
'a' : evens "cde"
-----------
First pattern of evens
, adding 'c'
:
'a' : evens "cde"
-----------
-- evens (x : xs) = x : odds xs
-- x = 'c'
-- xs = "de"
-- evens "cde" = 'c' : odds "de"
'a' : 'c' : odds "de"
---------------
First pattern of odds
, ignoring 'd'
:
'a' : 'c' : odds "de"
---------
-- odds (_ : xs) = evens xs
-- xs = "e"
-- odds "de" = evens "e"
'a' : 'c' : evens "e"
---------
First pattern of evens
, adding 'e'
:
'a' : 'c' : evens "e"
---------
-- evens (x : xs) = x : odds xs
-- x = 'e'
-- xs = "" = []
-- evens "e" = 'e' : odds ""
'a' : 'c' : 'e' : odds ""
-------------
Now, finally, the first pattern of odds
doesn’t match, because an empty list []
doesn’t match the list constructor _ : _
, so we fall through to the second (default) pattern:
'a' : 'c' : 'e' : odds ""
-------
-- odds _ = []
-- odds "" = []
'a' : 'c' : 'e' : []
--
Giving the final result:
"ace"
Basically these functions are treating the input as a “stream” of values and producing a stream as a result: evens
consumes one element and outputs it to the result, proceeding to take the odds
of the remainder; while odds
consumes one element and discards it, taking the evens
of the remainder.
This doesn’t do any calculation on the indices because it doesn’t have to—it’s just following the structure of the list. By definition, the first value in a list is at an even index (when counting from 0), so evens
keeps it and takes the odd indices of the remainder, while odds
discards it and takes the even indices of the remainder. Removing each element shifts all the indices down by one—that is, the element that was at index 1
in the input list is at index 0
in the tail of the input:
zip [0..] "abcde" == [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
'a' 'b' 'c' 'd' 'e'
0 1 2 3 4
| | | | |
x / / / /
/ / / /
/ / / /
/ / / /
| | | |
'b' 'c' 'd' 'e'
0 1 2 3
zip [0..] "bcde" == [(0, 'b'), (1, 'c'), (2, 'd'), (3, 'e')]
You could also implement these functions explicitly using indices instead of mutual recursion. With list comprehensions:
evens xs = [x | (i, x) <- zip [0..] xs, even i]
odds xs = [x | (i, x) <- zip [0..] xs, odd i]
Or with explicit map
and filter
:
evens = map snd . filter (even . fst) . zip [0..]
odds = map snd . filter (odd . fst) . zip [0..]
Then you could even turn the predicate on the indices into a parameter:
indices f = map snd . filter (f . fst) . zip [0..]
evens = indices even
odds = indices odd
evens ('a':'b':'c':[])
, by hand? At what point does your understanding fail? – Alfaroevens
,x
is the element at the zero-th position, andxs
are the elements from the first position onwards. In the call toodds xs
, we moved one element forward through that. Instead of two functions, you could also write a single function with a "boolean index" - a toggle whether you currently are at an odd or an even position in the original array. – Alfarolength
orsum
ortake
)? – Alfarolength
,take
, and functions that return the 0th, 1st or 2nd element:head
,first
,second
). You probably need to learn more about pattern matching. – AlfaroData.Tree
, and several key places inStreaming
, and in the default definitions ofsome
andmany
. I wouldn't be at all surprised to see it elsewhere in parsing-land either, since automaton states can be represented as functions pretty naturally. – Psychological