LINUX.ORG.RU

Я не понимаю хвостовую рекурсию в Haskell?


0

0

После безуспешных попыток понять, почему компилятор GHC не хочет воспринимать мои процедурки как хвостовую рекурсию, написал простейший пример, что мое понятие хвостовой рекурсии отличается от такогого у компилятора.

4 варианта кода, вычисляющих 3^(1024*256)

a_exp_n a 1 = a
a_exp_n a n = a * a_exp_n a (n - 1)

a_exp_n_tail a 0 x = x
a_exp_n_tail a n x = a_exp_n_tail a (n - 1) (x * a)

a_exp_n_log :: Integer -> Integer -> Integer
a_exp_n_log a 1 = a
a_exp_n_log a n = if (n `mod` 2) == 0 then a_exp_n_log (a * a) (n `div` 2)
		else a * a_exp_n_log a (n - 1)

a_exp_n_log_tail a 0 x = x
a_exp_n_log_tail a n x = if (n `mod` 2) == 0 then a_exp_n_log_tail (a * a) (n `div` 2) x
			else a_exp_n_log_tail a (n - 1) (x * a)
			
main1 = putStr (show (a_exp_n 3 (1024*256)))
main2 = putStr (show (a_exp_n_tail 3 (1024*256) 1))
main3 = putStr (show (a_exp_n_log 3 (1024*256)))
main4 = putStr (show (a_exp_n_log_tail 3 (1024*256) 1))

maincmp = putStr (show ((c == (a_exp_n_tail 3 (1024*256) 1)) && (c == (a_exp_n_log 3 (1024*256))) && (c == (a_exp_n_log_tail 3 (1024*256) 1))))
	where c = (a_exp_n 3 (1024*256))

main = main4

по времени этозаняло:

wieker@localhost:~/Projects/all/projects/hellows/haskell/1$ time ./ver1 > /dev/null

real    0m33.524s
user    0m33.278s
sys     0m0.244s
wieker@localhost:~/Projects/all/projects/hellows/haskell/1$ time ./ver2 > /dev/null

real    0m43.517s
user    0m43.219s
sys     0m0.292s
wieker@localhost:~/Projects/all/projects/hellows/haskell/1$ time ./ver3 > /dev/null

real    0m0.111s
user    0m0.104s
sys     0m0.008s
wieker@localhost:~/Projects/all/projects/hellows/haskell/1$ time ./ver4 > /dev/null

real    0m0.116s
user    0m0.112s
sys     0m0.000s
wieker@localhost:~/Projects/all/projects/hellows/haskell/1$

то есть видно, что вариант с хвостовой рекурсией работает медленнее.
если же вычислять число 3^(1024*512), то первые 2 вариант отваливаются по stack overflow. два последних естествено работают. что я далаю не так? почему _tail-варианты жрут память?
★★
Ответ на: комментарий от stassats

>Да, и кто учил так называть функции?

а для примера, составленного специально для лора надо было постараться получше?

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

a_exp_n_tail - "хвостовой по-моему" вариант обычной функции для возведения a в степень n.

a_exp_n_log - алгоритм возводящий a^n за O(logN). а a_exp_n_log_tail - моя попытка его реализвать через хвостовую рекурсию.

main* -соответственно служебные функции.

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

Транклюкируй его немедленно!

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

Ты знал, на что идёшь! Надо было предварительно одевать каску, бушлат, кирзовые сапоги и презерватив :)

mv ★★★★★
()

Ты напоролся на ленивость. Попробуй a_exp_n_tail a n x = a_exp_n_tail a (n - 1) $! (x * a)

Вкратце: рекурсия-то хвостовая, но вот результат конструируется в виде громадного выражения, которое потом ещё приходится сворачивать. Да оно ещё и память занимает.

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