Почему многие функции Clojure варьируются? - PullRequest
12 голосов
/ 19 октября 2011

Вот проблема, с которой я сталкиваюсь в Clojure:

user=> (max [3 4 5 6 7])
[3 4 5 6 7] ; expected '7'

Некоторые функции не выполняют то, что я ожидаю!
Вот одно из решений, использующее apply:

user=> (apply max [3 4 5 6 7])
7

Другими примерами являются concat и min.
Мой вопрос, как новичок Clojure, почему эти функции являются вариативными? Я ожидал, что они будут работать над последовательностями. Используете ли apply лучший / идиоматический способ получить то, что я хочу?


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


Редактировать: Я думаю, что первоначальный вопрос был неясным. Вот что я имел в виду:

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

Для этих операций часто используются два варианта:

1) объединение двух элементов с использованием функции, которая принимает два аргумента
2) объединение от 0 до n элементов с использованием функции, которая принимает список (или последовательность) элементов

Функция для второго случая может быть построена из функции для первого случая (часто с использованием reduce).

Однако Clojure добавляет третий вариант использования:

3) объединение от 0 до n элементов с использованием функции с переменными числами

Итак, вопрос в том, почему Clojure добавляет этот третий случай?
Ответ Павла указывает, что:

  • это позволяет более гибкому коду
  • действуют исторические силы

Ответы [ 2 ]

15 голосов
/ 19 октября 2011

1) Удобство. С математическими функциями, такими как +, было бы неприятно оборачивать все в последовательности, когда вы просто пытаетесь выполнить некоторые вычисления.

2) Эффективность. Упаковка всего с коллекциями / последовательностями также была бы неэффективной, сначала необходимо создать последовательность, а затем распаковать ее во время выполнения вместо того, чтобы искать нужную функцию Java во время компиляции .

3) Ожидания. Вот как эти функции работают в других Лиспах и аналогично, с несколько другим синтаксисом, в других функциональных языках, так что это разумное ожидание для людей, приезжающих в Clojure. Я бы сказал, что идиоматический способ применения таких функций, как + к последовательности, в других функциональных языках заключается в использовании reduce или foldl / foldr, так что это также соответствует тому, как Clojure обрабатывает это.

4) Гибкость. Тот факт, что эти функции могут использоваться с функциями более высокого порядка, такими как map, делает их более удобными для использования, если они являются вариативными. Скажем, у вас есть три вектора, и вы хотите применить функцию к элементам в одной и той же позиции. Если ваша функция является переменной, то вы можете просто использовать карту с несколькими коллекциями (тогда карта также должна быть переменной;)):

(map + [1 2 3 4] [2 3 4 5] [3 4 5 6])
; [6 9 12 15]

Это намного удобнее, чем если бы все эти функции просто собирали коллекции.

Идиоматическое использование: (Отредактировано после превосходного комментария котарака)

Это зависит от функции, если вы должны использовать reduce или apply.

Для математических функций (+,-,*,/,etc.), которые принимают 2 аргумента в мире Java reduce, имеет больше смысла, так как он может напрямую использовать версию Java с 2 аргументами. С apply они как бы делают неявное reduce ( Функция добавляет два аргумента, а затем возвращается с результатом, следующим аргументом и остальными.

Для str использование apply, вероятно, более эффективно. Когда str вызывается с более чем одним аргументом, он создает StringBuilder, добавляет к нему все аргументы, а затем создает строку. При использовании reduce StringBuilder будет создан n-1 раз и будет добавлять только одну строку каждый раз. Это как в Шлемиеле * шутке маляра , что приводит к сложности O (n ^ 2).

Пока вердикт: использование apply с математическими функциями не повредит, но использование reduce с str может быть довольно дорогим.

3 голосов
/ 19 октября 2011

Алекс Миллер только что написал о своей проблеме в своем блоге: 2 - это запах

...