问题
This is the problem given: What are the first 8 elements in the following list?
mystery = 0 : 10 : (map(+1)mystery)
The answer is [0,10,1,11,2,12,3,13...]
But in my opinion the answer should be [0,10,1,11,1,11,2,12]
. The following steps show why:
1) We are given ;list [0,10]
so after applying the function the first time we have the list [0,10,1, 11]
2) Now we have a list [0,10,1,11]
so after applying the function again the resulting list should be [0,10,1,11,1,11,2,12]
Apparently that is not the case. Can anyone explain why?
回答1:
Before diving into the definition of mystery
, let's take a look at one of the laws map
obeys:
map f (map g xs) == map (f . g) xs
A rather informal proof of this law is easy to follow:
map f (map g [x1, x2, ..., xn]) == map f [g x1, g x2, ..., g xn]
== [f (g x1), f (g x2), ..., f (g xn)]
== [(f.g) x1, (f.g) x2, ..., (f.g) xn]
== map (f.g) [x1, x2, ..., xn]
With that in mind, let's expand mystery
step by step:
mystery == 0 : 10 : map (+1) mystery
-- by definition of mystery
== 0 : 10 : map (+1) (0 : 10 : map (+1) mystery)
-- by definition of map and the fact that 0 + 1 == 1
== 0 : 10 : 1 : map (+1) (10 : map (+1) mystery)
-- by definition of map and the fact that 10 + 1 == 11
== 0 : 10 : 1 : 11 : map (+1) (map (+1) mystery)
-- using the law above, and the fact that (+1) . (+1) == (+2)
== 0 : 10 : 1 : 11 : map (+2) mystery
== 0 : 10 : 1 : 11 : map (+2) (0 : 10 : map (+1) mystery)
== 0 : 10 : 1 : 11 : 2 : map (+2) (10 : map (+1) mystery)
== 0 : 10 : 1 : 11 : 2 : 12 : map (+2) (map (+1) mystery)
== 0 : 10 : 1 : 11 : 2 : 12 : map (+3) mystery
-- etc
You don't start with a finite list [0, 10]
; you start with an infinite list that begins with 0 and 10, with the remaining elements defined recursively.
In some sense, there is no closed form for the list, but that doesn't matter; laziness means that you only apply map
to mystery
as needed to get requested items. For example, neither head mystery
nor head (tail mystery)
ever need to evaluate the call to map
, and head (tail (tail mystery))
only needs to map (+1)
to the head mystery
, not the entire infinite list.
Laziness blurs the distinction between the list and the algorithm to compute the list.
回答2:
Let's just work through it, using the recursive definition of map
:
map _ [] = []
map f (x:xs) = f x : map f xs
Since we have
mystery = 0:10:(map (+1) mystery)
we already know that
mystery = [0, 10, ...]
and the ...
stands for map (+1) mystery
. So let's use the definition above to calculate it.
The list we're applying it to clearly isn't empty - it starts with 0 and 10. So we use the second line, with x
as 0 and xs
as 10:(map (+1) mystery)
:
map (+1) mystery = 1:(map (+1) (10:(map (+1) mystery)))
or, using the formula again for the first level of nesting:
map (+1) mystery = 1:11:(map (+1) (map (+1) mystery))
So, going back to mystery
itself, we now know its first 4 elements:
mystery = [0, 10, 1, 11, ...]
and the ...
stands for the contents of map (+1) (map (+1) mystery)
. That is, based on the result above:
map (+1) (1:11:(map (+1) (map (+1) mystery)))
I'll spare you the details of the evaluation here, because it should be clear by now what happens: the first 2 elements (which will be the 5th and 6th in mystery
) will be 2 and 12, and the rest will be map (+1) ((map (+1) (map (+1) mystery)))
. Which, by exactly the same process, will start with 3 and 13. And so it goes on, as far as you could ever care to calculate it.
回答3:
Since
mystery = 0 : 10 : map (+1) mystery
by the definitions of (!!)
and (:)
and map
it is the case that
mystery !! 0 = 0 -- pseudocode
mystery !! 1 = 10
mystery !! n | n > 1
= (0 : 10 : map (+1) mystery) !! n
= (10 : map (+1) mystery) !! (n-1)
= (map (1+) mystery) !! (n-2)
= 1 + (mystery !! (n-2))
and there's your answer.
An illustration:
-- 0 1 2 3 4 5 6 -- n
mystery = [0, 10, 1, 11, 2, 12, 3, ...
-- / / / / / -- (+1)
-- [0, 10, 1, 11, 2, ...
-- 0 1 2 3 4 -- n-2
so all this is, is each element being defined with the reference to a previous one, two positions prior.
Thus another way to write the same thing down, making the pairing up (nay zipping) explicit, is
mystery = 0 : 10 : zipWith (+) (repeat 1)
mystery
-- and in general,
-- map (f y) xs == zipWith f (repeat y) xs
Translated into an imperative pseudocode, the program
main = print mystery
is actually the same as
main :
a = 0
b = 10
print "["
while( True ) :
print a ,
print b ,
a += 1
b += 1
A principled approach to tackle such problems is to name all your interim entities, the inner thunks behind lazy data constructors (here, :
), as they come into being, forced into WHNF by the demands of lazy evaluation. Then the mystery disappears:
mystery = 0 : 10 : map (+1) mystery
= x1 : t1
where
x1 = 0
t1 = 10 : map (+1) mystery
= x2 : t2
where
x2 = 10
t2 = map (+1) mystery
= map (+1) (x1 : t1)
= x1 + 1 : map (1+) t1 -- by definition of map
= x3 : t3
where
x3 = x1 + 1 = 0 + 1 = 1
t3 = map (1+) t1 = map (1+) (x2 : t2)
= x2 + 1 : map (1+) t2 -- = x_{n} + 1 : ...
= x4 : t4 -- = x_{n+2} : ...
where
....
At no point do we get any function other than (1+)
in this reduction process, nor do we ever get more than one of them stacked up in a row.
All we get is xn := 1 + xn-2
, repeatedly, for all n
above 1
.
来源:https://stackoverflow.com/questions/56512596/why-is-this-recursive-map-function-only-being-applied-to-the-last-two-elements-i