2010-04-26 2 views
8

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

Есть ли аналогичные объекты в Clojure по отношению к loop/recur?

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

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

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

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

ответ

4

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

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

+3

Этот ответ кажется мне вводящим в заблуждение. Точка цикла/рекурсия - это как раз ограниченная форма TCO, а именно TCO для саморекурсии (в функции или в форме цикла). Полностью общая TCO также будет оптимизировать использование стека для хвостовых вызовов для других функций. Таким образом, хотя у Clojure нет TCO (общая форма), у него есть собственные вызовы TCO из recur. Это то, о чем говорит «рекурсивный цикл цикла с постоянным пространством». В конечном счете, recur делает то же самое, что и @tailrec Scala, о чем и идет речь. (Здесь надеется, что этот вопрос уйдет с JDK7.) –

+0

Думаю, вы не использовали Scala, что очень простая оптимизация хвоста происходит даже без tailrec, но вы получите сообщение об ошибке, если вы отметите метод как tailrec, то есть не на самом деле хвост-вызов рекурсивный. Ваш комментарий гораздо более вводящий в заблуждение, чем мой ответ, но каждый имеет право на мнение ... –

+0

Итак, ваше мнение таково: «[t] здесь нет оптимизации хвостового вызова при использовании цикла/повтора», хотя сразу после сообщения вы цитируете отрывок из документов (как вы говорите), в котором говорится, что цикл с помощью 'recur' не потребляет стек? Кроме того, этот вопрос не касается поведения 'scalac', когда' @ tailrec' не указан, речь идет о возможности получения в Clojure рода гарантий '@ tailrec' дает вам Scala (вполне возможно, просто используйте' recur '). –

5

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

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

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

+0

Я не думаю, что это правильно. 'recur' _instructs_ компилятор Clojure, что вызов является хвостовым рекурсивным, тогда как' @ tailrec' _asks_ компилятор Scala, если вызов является хвостовым рекурсивным. Поскольку Scala часто не может гарантировать, что вызов является хвостовым рекурсивным (например, для неконфигурированных методов), нужно иметь возможность более оптимизировать хвостовую рекурсию с Clojure, чем с помощью Scala, за счет того, что она должна быть явной. –

+0

Конечно, можно утверждать, что Scala также имеет явный механизм оптимизации хвостовой рекурсии, например, путем введения вложенного метода. Но это не так очевидно/идиоматично, как в Clojure. –

+0

Хм. Я думал, что '@ tailrec' должен был заставить компилятор выходить из строя всякий раз, когда аннотированный метод не мог быть превращен в цикл. [Соответствующая страница документации] (http://www.scala-lang.org/api/current/scala/annotation/tailrec.html), похоже, подтверждает это (как и быстрый эксперимент с аннотацией '@ tailrec', -трехово-рекурсивный факторный метод). «Recur» Clojure вызывает одно и то же поведение («recur' в не-хвостом положении является ошибкой и заставляет компилятор жаловаться). Я не эксперт Scala, хотя (не длинным выстрелом!), Так что, возможно, я пропустил какую-то важную тонкость здесь ...? –

Смежные вопросы