рекурсивного вызова main. Стало интересно на счет переполнения стека из-за такой рекурсии
Нашел в интернете такой вопрос на stackoverflow:
https://stackoverflow.com/questions/9015318/what-to-use-instead-of-a-main-loop-in-haskell
И в первом ответе сказано про отсутствие проблем, так как действие будет отброшено.
Как понимать эти слова? Что именно будет отброшено? Что если внутрь do добавить что-нибудь в духе
str <- getLine
str будет иметь тип String, а не IO String, будет ли она "засорять" стек?
Или я рано задался этим вопросом и нужно сперва дойти до монад и т.п.?
https://ru.m.wikipedia.org/wiki/%D0%A5%D0%B2%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%B0%D1%8F_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F
А как понять, когда хвостовая рекурсия есть, когда ее нету. В смысле когда компилятор это использует. Правильно ли я понимаю, что такое может быть реализовано только для функций вида (пишу не на haskell) def f(...): ... return f(...) Но не для def f(...): ... return 1 + f(...) Так как тут, по сути, f(...) используется в контексте 1+... ?
для нехаскеля правильно. в Хаскеле код совсем иначе придётся
обычно понять очень легко. если результат функции описывается вызовом функции, то это хвостовой вызов
хвостовой вызов сильно проще монад, и не требует монад
Я просто думал, что разгадка кроется, например, в монадах. Не знаю пока что что это такое, но очень наслышан.
Вот, например, найдет ли в следующем коде компилятор хвостовую рекурсию? lenImpl :: [a] -> Int -> Int lenImpl [] n = n lenImpl (x:xs) n = lenImpl xs (n+1) len :: [a] -> Int len x = lenImpl x 0 Правильно ли я понимаю, что тут все равно остается проблема ленивости вычислений и я буду иметь дело с выражением (((1+1)+1)...)?
нет. скорее, наоборот, монады эффективно работает благодаря хвостовой оптимизации
компилятору не надо её искать. хвостовой вызов — это просто самый внешний вызов
правильно понимаете. не стэк вызовов будет занимать память, а граф задумок
но в данном случае легко заставить сумму вычисляться на месте
Если не затруднит, не могли бы привести как реализовать данную оптимизацию. Или речь идет про bang pattern?
на самом деле не вызов, а применение. в ленивом языке есть отличие между ними
да lenImpl (x:xs) !n = ...
А что такое вызов тогда и в чем отличие?
Я не эксперт и лучше дождаться мнения эксперта, но, на сколько я понимаю, это различие становится существенным, когда речь заходит о частичном применении.
вызов — это переход к исполнению кода функции. когда функция применяется к аргументам, сначала создаются задумки, но вычисление не происходит. когда требуется получить значение задумки, происходит вызов
А применение функции создает задумку? Понятно.
Обсуждают сегодня