LINUX.ORG.RU

[scheme] рекурсия против итерации

 


0

0

Привет все. Тут такое дело, чего то я недопонимаю ...

Имеется две реализации !

% cat tttt.scm
#!/usr/bin/env gsi-script

(define factorial-1
 (lambda (n)
  (if (zero? n)
   1
   (* n (factorial-1 (- n 1))))))

(define factorial-2
 (lambda (n)
  (let iteration ((i n)
                  (p 1))
   (if (zero? i)
    p
    (iteration (- i 1) (* p i))))))

(time (factorial-1 16384))
(newline)
(time (factorial-2 16384))

Причем вторая работает медленнее, хотя я ожидал что она будет
быстрее.

% ./tttt.scm
(time (factorial-1 16384))
    1376 ms real time
    1297 ms cpu time (891 user, 406 system)
    139 collections accounting for 417 ms real time (273 user, 141 system)
    206481088 bytes allocated
    62384 minor faults
    no major faults

(time (factorial-2 16384))
    1844 ms real time
    1734 ms cpu time (1242 user, 492 system)
    762 collections accounting for 818 ms real time (625 user, 141 system)
    228843664 bytes allocated
    67161 minor faults
    no major faults

Как же tail call optimisation и все такое? Поясните если можно.

А где ты итерацию увидел? Во втором случае создаётся такая же лямбда, только у нёё 2 параметра вместо одного, вот и работает чуть дольше. Суть tail-call заключается в том, что рекурсия не ест бессмысленно стек.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Вообще, как я понимаю, в первом случае расход памяти O(n), во втором O(1), скорость тут по идее не при чём. Если где то неправ - поправьте, тоже интересно.

Legioner ★★★★★
()
Ответ на: комментарий от asgard

> с чего вы взяли, что во втором случае o(1)? там то же самое - чем больше n, тем дольше будет работать. o(n)

прошу прощения, торможу. пропустил ключевые слова - "расход памяти".

asgard
()
Ответ на: комментарий от Legioner

> А где ты итерацию увидел?
Ну во втором случае tail call optimisation будет, так? Т.е. это будет
итеративный процесс. Состояние вычисление будет передаваться как параметр, а не складываться на стек.

binnehex
() автор топика
Ответ на: комментарий от dilmah

> господа, не путаем О большое и о малое

знаем, просто есть нехорошая писать с маленькой буквы все слова и символы.

asgard
()
Ответ на: комментарий от binnehex

> Ну во втором случае tail call optimisation будет, так? Т.е. это будет итеративный процесс. Состояние вычисление будет передаваться как параметр, а не складываться на стек.

в первом случае тоже ничего в стек кидаться не должно, бо хвостовая рекурсия.

asgard
()
Ответ на: комментарий от anonymous

(* n (factorial-1 (- n 1))))))

вычисляется так:
1. n -> n
2. (- n 1) ; вызов функции
3. (factorial-1 <результат 2>) ; вызов функции (рекурсивный, не хвостовой)
4. (* <результат 1> <результат 2>) ; вот здесь идёт tail-call, но это особого выигрыша не даёт.

Legioner ★★★★★
()

Сори, я не лиспер, но как работает такой вариант: (define factorial-back (lambda (f, n) (if (zero? n) f (factorial-back (* f n) (- n 1)))))

(define factorial (lambda (n) (factorial-back 1 n )))

?

funny_falcon
()
Ответ на: комментарий от funny_falcon

> Сори, я не лиспер, но как работает такой вариант
Тут хвостовая рекурсия будет, т.к. рекурсивный вызов находится в
_хвосте_ вычисления, и от этого вызова в текущем вызове
factorial-back ничего не зависит. Т.е. текущий call frame на
стеке может быть просто заменен на новый. Стек зазря не жрется.

А почему bytes allocated так отличаются?

binnehex
() автор топика

Я вообще не очень понимаю, как у тебя получается считать 16384!, это же дочерта :) Мой csi дает стабильно +inf за тысячную долю секунды.

В твоем случае разница, скорее всего, из-за специфической реализации твоего интерпретатора.

swizard
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.