metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2010-09-13 05:45 pm
Entry tags:

Принудительная хвостовая рекурсия: жрать память вместо стека?

Читаю про катаморфизмы и FSharp. Вообще, изначально это я искал ответ на этот вопрос, но зачитался и забыл.

Там приводится пример, как выразить foldBack (оно же foldr) через fold(оно же foldl) с помощью continuations. И выглядит это как будто мы заменяем использование стека в случае не-tail-recursive функции таким же использованием памяти из head. Причем в случае стека выделение и удаление памяти достаточно дешевое, а вот что будет в случае обычной памяти - вопрос загадочный. Непонятно, есть ли тогда какие-нибудь преимущества у такого преобразования не-tail-recursive функции в tail-recursive?

[identity profile] antilamer.livejournal.com 2010-09-13 05:56 pm (UTC)(link)
Нет никаких преимуществ, обыкновенное извращение.

[identity profile] plumqqz.livejournal.com 2010-09-14 06:18 am (UTC)(link)


"От Радослава ко Хотеславу. возьми у прасола 2 гривене и 5 куно". (1-я стр.).
"Якове брате, еби лежа, ебехото, аесово".(2-я строчка)


"Радослав дает указание своему брату Хотеславу: "Возьми у прасола (торговца) 2 гривны и 5 кун". Это указание Хотеславу резко не понравилось; возможно, брат отсылает его к прасолу вместо того, чтобы просто отдать долг (есть аналогии этой ситуации в новгородской грамоте 690). Он ответил язвительно и не стесняясь формой выражения. Обращение - "Якове брате" (а не Радославе) - по-видимому, ироническое или даже саркастическое. Хотеслав называет брата не мирским, а крестильным именем, да еще со словом брате; надо полагать, это было уместно лишь в церковной или в особо торжественной ситуации, но никак не в сочетании с последующей грубостью. Примерный смысл ответа - "не оригинальничай" (веди себя, как все).

[identity profile] metaclass.livejournal.com 2010-09-14 06:46 am (UTC)(link)
Отлично, можно на работе использовать :)

[identity profile] dsorokin.blogspot.com (from livejournal.com) 2010-09-14 09:19 am (UTC)(link)
Иногда все же есть смысл, потому что .NET поддерживает TCO. Только есть нюансы, когда такая оптимизация не работает.

[identity profile] antilamer.livejournal.com 2010-09-14 09:27 am (UTC)(link)
То-то и оно, что вообще никакого смысла нет. При применении полученного continuation'а все равно стек будет наворачиваться.

[identity profile] dsorokin.blogspot.com (from livejournal.com) 2010-09-14 12:03 pm (UTC)(link)
Не совсем понял. В данном примере из той статьи смысла нет или, вообще, стек наворачивается всегда?

Кстати, в исходниках Async есть забавный комментарий (там продолжения используются):

"F# don't always take tailcalls to functions returning 'unit' because this is represented as type 'void' in the underlying IL. Hence we don't use the 'unit' return type here, and instead invent our own type."

[identity profile] antilamer.livejournal.com 2010-09-14 12:05 pm (UTC)(link)
Я не вглядывался в эту конкретную статью, но я помню другую статью, где тоже foldr выражался через foldl или наоборот.
Там вместо того, чтобы сделать нормальный foldr, с помощью foldl строилось замыкание, которое потом сделает foldr.
Так вот, это замыкание для своего *выполнения* требует такого же линейного объема *стека*, а не кучи (а само по себе еще и занимает место в куче).

[identity profile] dsorokin.blogspot.com (from livejournal.com) 2010-09-14 01:02 pm (UTC)(link)
Ну, бывает. Тут засады возможны.

Посмотрел еще раз пример FoldBack. Вроде бы, выглядит логично (но куда проще было бы взять и переписать все через надежный Async :). Только unit как возвращаемый тип несколько беспокоит меня. Если я правильно понял тот комментарий разработчиков F#, то такое продолжение как в статье может запросто съесть и стек на некоторых версиях .NET, а может и сработать как надо.

[identity profile] dsorokin.blogspot.com (from livejournal.com) 2010-09-15 04:21 am (UTC)(link)
Кстати, туплю... Стек не должен съедаться. Функция FoldBack полиморфна, а потому тип unit будет заменен компилятором на внутренний тип Unit. Тогда значение () просто станет null. То есть, злобного void'а не будет. Условия для TOC будут благоприятными.

[identity profile] blacklion.livejournal.com 2010-09-13 05:59 pm (UTC)(link)
Стек, особенно в 32-бит мультитреде, может быть куда меньше хипа.

[identity profile] gds.livejournal.com 2010-09-13 06:12 pm (UTC)(link)
стек бывает ограничен жостко (обычно в опциях линкера устанавливается), тогда как хип растёт согласно имеющейся памяти (а то и своп используется, если есть).
Например, кое-где я использую формально типо-небезопасную библиотеку (хотя там с типами всё хорошо, но не доказано), но чуть более медленную, зато tail-recursive на всех функциях, работающих со списками.

[identity profile] raydac.livejournal.com 2010-09-13 06:31 pm (UTC)(link)
в случае рекурсии прикинь какой у тебя отладочный стек вызовов JVM попытается построить для объекта Exception и это при нехватке памяти то, так что разница есть

[identity profile] theiced.livejournal.com 2010-09-13 07:18 pm (UTC)(link)
фиолетовый крокодильчики летать

[identity profile] usovalx.livejournal.com 2010-09-13 07:26 pm (UTC)(link)
Не знаю как там с производительностью аллокатора в .Net, но в том-же ocaml выделение памяти в куче чудовищно быстрое.

[identity profile] gds.livejournal.com 2010-09-13 08:34 pm (UTC)(link)
но на стеке -- быстрее, поэтому стандартный List.map выигрывает по скорости у алгоритма, выделяющего на хипе и далее небезопасно (Obj) меняющего cons cell. (хотя, в целом, всё получается безопасно, кодэ см. в extLib.)
Собственно, потому и не меняют List.map в stdlib, что текущая версия на мелких списках чудо как хороша. (а именно, с текущим мусорщиком лучше не сделать.)
А для больших объёмов рекомендуют не списки, а что-то побыстрее.

[identity profile] mchrome-cat.livejournal.com 2010-09-14 07:43 pm (UTC)(link)
Кстати, если не ошибаюсь, в chicken scheme первое поколение кучи на стеке и лежит.

[identity profile] migmit.livejournal.com 2010-09-13 09:01 pm (UTC)(link)
Ну, появляется возможность делать call/cc.

[identity profile] usovalx.livejournal.com 2010-09-13 09:28 pm (UTC)(link)
Если я правильно понимаю migmit, то продолжение в случае call/cc выделяется на хипе и может быть передано вверх.

[identity profile] migmit.livejournal.com 2010-09-14 04:56 am (UTC)(link)
Если не ошибаюсь, речь идёт о CPS transform. Коли так, см. The 90 minutes Scheme to C compiler.

[identity profile] mchrome-cat.livejournal.com 2010-09-14 07:39 pm (UTC)(link)
Кстати, существует книжка от Andrew Appel - "Compiling with continuations", где он по идее (я сам не читал) подробно рассказывает, зачем нужен cps.