Подтвердить что ты не робот

Почему многие функции Clojure вариативны?

Здесь проблема, с которой я сталкиваюсь в 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 лучший/идиоматический способ получить то, что я хочу?


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


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

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

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

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

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

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

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

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

  • это позволяет более гибкий код
  • Есть исторические силы на работе
4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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