Я понимаю концептуальное различие между reduce
и apply
:
(reduce + (list 1 2 3 4 5))
; translates to: (+ (+ (+ (+ 1 2) 3) 4) 5)
(apply + (list 1 2 3 4 5))
; translates to: (+ 1 2 3 4 5)
Однако, какой является большим количеством идиоматического clojure? Это имеет много значения так или иначе? От моего (ограниченного) тестирования производительности это кажется reduce
немного быстрее.
reduce
и apply
, конечно, эквивалентны (с точки зрения возвращаемого конечного результата) только для ассоциативных функций, которым необходимо видеть все свои аргументы в переменной- случай арности. Когда они эквивалентны по результату, я бы сказал, что apply
всегда совершенно идиоматичен, в то время как reduce
эквивалентен - и может сократить долю секунды на мгновение - - во многих распространенных случаях. Ниже приводится мое обоснование верить в это.
+
сам по себе реализован в терминах reduce
для случая переменной арности (более 2 аргументов).В самом деле, это кажется чрезвычайно разумным способом «по умолчанию» для любой ассоциативной функции переменной арности: reduce
имеет потенциал для выполнения некоторых оптимизаций для ускорения работы - возможно, с помощью чего-то вроде internal-reduce
, новинка 1.2, недавно отключенная в master, но, надеюсь, будет повторно представлена в будущем - что было бы глупо повторять во всех функциях, которые могли бы получить от них пользу в случае vararg. В таких общих случаях apply
просто добавит немного накладных расходов. (Обратите внимание, что это не повод для беспокойства.)
С другой стороны, сложная функция может использовать преимущества некоторых возможностей оптимизации, которые недостаточно общие, чтобы быть встроенными в reduce
; тогда apply
позволит вам воспользоваться ими, в то время как reduce
может действительно замедлить вас. Хороший пример последнего сценария, встречающегося на практике, - это str
: он использует StringBuilder
внутри и значительно выиграет от использования apply
, а не уменьшить
.
Я бы сказал, что если есть сомнения, используйте примените
; и если вы знаете, что это ничего не дает вам сверх reduce
(и что это вряд ли скоро изменится), не стесняйтесь использовать reduce
, чтобы избавиться от этих крошечных ненужных накладных расходов. если хочешь.
Мнения различаются - В большом мире Лиспа reduce
определенно считается более идиоматическим. Во-первых, уже обсуждались различные вопросы. Кроме того, некоторые компиляторы Common Lisp фактически не работают, когда apply
применяется к очень длинным спискам из-за того, как они обрабатывают списки аргументов.
Однако среди клоюристов в моем кругу использование apply
в этом случае кажется более распространенным. Мне легче грокнуть, и я тоже предпочитаю это.
В этом случае это не имеет значения, потому что + - это особый случай, который может применяться к любому количеству аргументов. Reduce - это способ применения функции, которая ожидает фиксированное количество аргументов (2) к сколь угодно длинному списку аргументов.
Обычно я предпочитаю сокращение при работе с любым типом коллекции - он работает хорошо и в целом является довольно полезной функцией.
Основная причина, по которой я бы использовал apply, заключается в том, что параметры означают разные вещи в разных позициях или если у вас есть пара начальных параметров, но вы хотите получить остальные из коллекции, например
(apply + 1 2 other-number-list)