2017-02-17 2 views
3

В Мышление функционально в Haskell (стр. 213) показаны две версии циклического списка. Один оценивает в квадратичное время:Оценка предложения where в циклических списках

iterate3 f x = x:map f (iterate3 f x) 

iterate3 (2*) 1 
= 1:map (2*) (iterate3 (2*) 1) 
= 1:2:map (2*) (map (2*) (iterate3 (2*) 1)) 
= 1:2:4:map (2*) (map (2*) (map (2*) (iterate3 (2*) 1))) 

Другой, который использует where, оценивает линейное время:

iterate2 f x = xs where xs = x:map f xs 

iterate2 (2*) 1  
xs   where xs = 1:map (2*) xs 
= 1:ys  where ys = map (2*) (1:ys) 
= 1:2:zs where zs = map (2*) (2:zs) 
= 1:2:4:ts where ts = map (2*) (4:ts) 

Я не совсем понимаю эту оценку. Как x переназначается для каждого последующего элемента списка вместо 1 (как в первой версии)?

ответ

4

Важная деталь заключается в том, что xs в iterate2 определяется в терминах самого себя, создавая круговую структуру.
(Это иногда называют «связыванием узла».)

Визуализируя это как график, оценка выглядит примерно так (предупреждение: искусство ASCII).

xs where xs = 1 : map (2*) xs:

 : <----------+ 
/ \   | applies to 
    1 map (2*) --+ 

-->

 : 
/ \ 
    1  : <--------+ 
    / \   | applies to 
    2 map (2*) --+ 

-->

 : 
/ \ 
    1  : 
    / \ 
    2  : <---------+ 
     / \   | applies to 
     4 map (2*) --+ 

И так далее.

+0

Спасибо! Для меня поиск правильной визуализации часто является самым большим препятствием для понимания рекурсии. – planarian

+0

Один вопрос о последующем: Производит ли iterate3 циклический список? В этом вопросе текст немного неоднозначен. – planarian

+1

@planarian Я бы сказал, что сам список не имеет циклов - независимо от того, сколько его вы оцениваете, вы не будете «возвращаться» к предыдущему элементу, но вы можете утверждать, что его определение имеет один. (То есть, это «циклическое определение списка», но не «определение циклического списка».) – molbdnilo

1

Давайте раскатать рекурсию в iterate2:

xs = x : map f xs 
    = x : map f (x : map f xs)       -- Inline xs 
    = x : (f x) : map f (map f xs)      -- Definition of map 
    = x : (f x) : map f (map f (x : map f xs))   -- Inline xs 
    = x : (f x) : map f ((f x) : map f (map f xs))  -- Definition of map 
    = x : (f x) : (f (f x)) : map f (map f (map f xs))) -- Definition of map 
    ... 

Таким образом, вы можете увидеть, что список, возвращаемый iterate2 f x имеет x в качестве первого элемента, f x как второй, и так далее.

+1

Это сокращение семантически корректно, но не показывает, что это вычисляется в линейном времени - у нас все еще есть квадратичная сложность в этом сокращении. Это может смутить ОП. – chi

+0

Правильно, но я не знаю, как бы я показал, что, все еще отвечая на исходный вопрос о том, как «х» обновляется ». – Cactus

Смежные вопросы