Предупреждение о закрытии / ошибка при сбое оптимизации хвостового вызова - PullRequest
9 голосов
/ 26 апреля 2010

В Scala 2.8.x добавлена ​​новая аннотация (@tailrec), которая дает ошибку времени компиляции, если компилятор не может выполнить оптимизацию хвостового вызова для аннотированного метода.

Есть ли в Clojure аналогичное средство в отношении loop/recur?

EDIT: После прочтения первого ответа на мой вопрос (спасибо, Божидар Бацов) и дальнейшего поиска в документации Clojure, я наткнулся на это:

(повторяющиеся выражения *)
Оценивает выражения по порядку, а затем параллельно связывает привязки точки рекурсии со значениями выражений. Если точка рекурсии была методом fn, то она повторно связывает параметры. Если точка рекурсии была циклом, то она повторно связывает привязки цикла. Затем выполнение возвращается к точке рекурсии. Выражение recur должно точно соответствовать арности точки рекурсии. В частности, если точка рекурсии была вершиной метода variadic fn, сбор аргументов покоя отсутствует - должен быть передан один seq (или ноль). возврат в другое положение, кроме хвоста, является ошибкой .

Обратите внимание, что recur является единственной циклической конструкцией без использования стека в Clojure . Оптимизация хвостовых вызовов отсутствует, и использование самостоятельных вызовов для зацикливания неизвестных границ не рекомендуется. recur работает, и его использование в хвостовой позиции проверяется компилятором [выделено мной].

(def factorial
  (fn [n]
    (loop [cnt n acc 1]
       (if (zero? cnt)
            acc
          (recur (dec cnt) (* acc cnt))))))

Ответы [ 2 ]

6 голосов
/ 26 апреля 2010

На самом деле ситуация в Scala w.r.t. Оптимизация вызова хвоста такая же, как в Clojure : ее можно выполнить в простых ситуациях, таких как саморекурсия, но не в общих ситуациях, таких как вызов произвольной функции в хвостовой позиции.

Это связано с тем, как JVM работает - чтобы TCO работал на JVM, сама JVM должна была бы поддерживать его, чего в настоящее время нет (хотя это может измениться, когда JDK7 выпущен).

См. Например эта запись в блоге для обсуждения TCO и прыжков на батуте в Scala. Clojure имеет точно такие же функции, чтобы упростить рекурсию без использования стека (= оптимизирован хвостовой вызов); это включает в себя ошибку времени компиляции, когда пользовательский код пытается вызвать recur в не-хвостовой позиции .

4 голосов
/ 26 апреля 2010

При использовании цикла / рекурсивного AFAIK оптимизация хвостовых вызовов отсутствует. Цитата из официальных документов:

при отсутствии изменяемых локальных переменные, циклы и итерации должны принять другую форму, чем в языки со встроенным для или во время конструкции, которые контролируются меняющееся состояние В функционале Языковые циклы и итерации заменено / реализовано через рекурсив вызовы функций. Много таких языков гарантировать, что вызовы функций сделаны в хвостовая позиция не потребляет стек пространство, и, следовательно, рекурсивные циклы использовать постоянное пространство. С Clojure использует соглашения о вызовах Java, это не может и не делает то же самое Гарантии оптимизации вызовов. Вместо этого, он обеспечивает повторное специальное оператор, который делает постоянное пространство рекурсивный цикл повторным связыванием и прыгать в ближайшую замкнутую петлю или функциональная рамка. Пока не так вообще как tail-call-оптимизация, это позволяет большинство из того же элегантного конструирует и предлагает преимущество проверить, что звонки могут повториться происходят только в хвостовой позиции.

...