Я недавно начал прочитывать 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. Надо надеяться, точка будет ясна, даже если код будет неправильным.
Если у вас есть выбор, вы всегда должны выражать свои вычислительные намерения в наиболее абстрактных терминах. Это облегчает читателю понимание ваших намерений, а компилятору упрощает оптимизацию вашего кода. В вашем примере, когда компилятор тривиально знает, что вы выполняете операцию сворачивания, в силу того, что вы ее называете, он также тривиально знает, что он, возможно, может распараллелить операции листа. Компилятору будет намного сложнее понять это, когда вы пишете операции очень низкого уровня.
В 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))
Я возьму слегка субъективный вопрос и дам весьма субъективный ответ, поскольку Ира уже давала вполне прагматичный и логичный ответ. : -)
Я знаю, что явное написание вещей высоко ценится в некоторых кругах (разработчики 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
и друзья действительно хорошие абстракции.