уменьшить, или явная рекурсия?

Я недавно начал прочитывать Paul Graham На Lisp с другом, и мы поняли, что у нас есть совсем другие мнения, уменьшите: Я думаю, что это выражает определенный вид рекурсивной формы очень ясно и кратко; он предпочитает выписывать рекурсию очень явно.

Я подозреваю, что мы - каждое право в некотором контексте и неправильно в другом, но мы не знаем, где строка. Когда Вы выбираете одну форму по другому, и о чем Вы думаете при совершении того выбора?

Для соглашений, под чем я подразумеваю, уменьшают по сравнению с явной рекурсией, вот та же функция, реализованная дважды:

(defun my-remove-if (pred lst)
    (fold (lambda (left right)
                  (if (funcall pred left) 
                      right 
                      (cons left right)))
          lst :from-end t))

(defun my-remove-if (pred lst)
    (if lst
        (if (funcall pred (car lst))
            (my-remove-if pred (cdr lst))
            (cons (car lst) (my-remove-if pred (cdr lst))))
        '()))

Я боюсь, что начал Интригана (теперь, мы - Рэкетиры?) поэтому сообщите мне, испортил ли я синтаксис языка Common LISP. Надо надеяться, точка будет ясна, даже если код будет неправильным.

12
задан Wang 5 July 2010 в 21:01
поделиться

3 ответа

Если у вас есть выбор, вы всегда должны выражать свои вычислительные намерения в наиболее абстрактных терминах. Это облегчает читателю понимание ваших намерений, а компилятору упрощает оптимизацию вашего кода. В вашем примере, когда компилятор тривиально знает, что вы выполняете операцию сворачивания, в силу того, что вы ее называете, он также тривиально знает, что он, возможно, может распараллелить операции листа. Компилятору будет намного сложнее понять это, когда вы пишете операции очень низкого уровня.

11
ответ дан 2 December 2019 в 19:30
поделиться

В Common Lisp для обхода структуры данных, фильтрации и других связанных с этим операций предпочтение отдается функциям высшего порядка, а не рекурсии. Это также видно из многих предоставляемых функций, таких как REDUCE, REMOVE-IF, MAP и других.

Хвостовая рекурсия а) не поддерживается стандартом, б) может вызываться по-разному в разных компиляторах CL и в) использование хвостовой рекурсии может иметь побочные эффекты на генерируемый машинный код для окружающего кода.

Часто для определенных структур данных многие из этих операций реализуются с помощью LOOP или ITERATE и предоставляются как функции высшего порядка. Существует тенденция предпочитать новые расширения языка (такие как LOOP и ITERATE) для итеративного кода, чем использование рекурсии для итерации.

(defun my-remove-if (pred list)
   (loop for item in list
         unless (funcall pred item)
         collect item))

Вот также версия, использующая функцию Common Lisp REDUCE:

(defun my-remove-if (pred list)
  (reduce (lambda (left right)
            (if (funcall pred left) 
                right 
              (cons left right)))
          list
          :from-end t
          :initial-value nil))
4
ответ дан 2 December 2019 в 19:30
поделиться

Я возьму слегка субъективный вопрос и дам весьма субъективный ответ, поскольку Ира уже давала вполне прагматичный и логичный ответ. : -)

Я знаю, что явное написание вещей высоко ценится в некоторых кругах (разработчики Python считают это частью своего «дзен»), но даже когда я писал Python, я никогда не понимал этого. Я хочу писать на максимально высоком уровне и все время. Когда я хочу написать что-то явно, я использую язык ассемблера. Смысл использования компьютера (и HLL) в том, чтобы заставить его делать эти вещи за меня!

В вашем примере my-remove-if сокращение для меня выглядит нормально (кроме схем вроде fold и lst : - )). Я знаком с концепцией сокращения, поэтому все, что мне нужно понять, - это вычислить ваш f (x, y) -> z . Что касается явного варианта, мне пришлось на секунду подумать: я должен сам вычислить цикл. Рекурсия - не самая сложная концепция, но я думаю, что она сложнее, чем «функция двух аргументов».

Меня также не интересует повторение всей строки - (my-remove-if pred (cdr lst)) . Я думаю, что мне нравится Lisp отчасти потому, что я абсолютно безжалостен в DRY, а Lisp позволяет мне быть СУХИМ на осях, чего нет в других языках. (Вы можете добавить еще один LET вверху, чтобы избежать этого, но тогда он будет длиннее и сложнее, что, я думаю, является еще одной причиной предпочтения сокращения, хотя на данном этапе я мог бы просто рационализировать.)

Я думаю, что, возможно, контексты, в которых ребята из Python, по крайней мере, не любят неявную функциональность, были бы следующими:

  • когда никто не ожидал, что будет угадывать поведение (например, frobnicate ("привет, мир", True) - что означает True?) Или:
  • случаи, когда разумно изменить неявное поведение (например, когда аргумент True перемещается, или удаляется, или заменяется чем-то иначе, поскольку в большинстве динамических языков нет ошибки времени компиляции)

Но reduce в Lisp не соответствует обоим этим критериям: это хорошо понятная абстракция, которую все знают, и которая не будет изменения, по крайней мере, не в тех временных масштабах, которые меня волнуют.

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

3
ответ дан 2 December 2019 в 19:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: