Идиоматический способ передать имя метода для оценки в Clojure? - PullRequest
5 голосов
/ 11 августа 2010

Я передаю имя функции для использования в другом методе.

(defn mapper [m function]
  (cond
   (= '() m) '()
   true (cons (function (first m))
            (mapper (rest m) function))))

(println (mapper '((blue red)(green red)(white red)) #'first))

Есть ли более идиоматический способ сделать это в ближайшем будущем?

Ответы [ 3 ]

9 голосов
/ 11 августа 2010
  • Предпочитать векторы спискам. Вам не нужно указывать в кавычках большую часть времени, и он имеет лучшую производительность для многих вещей, таких как произвольный доступ. Списки используются в Clojure гораздо реже, чем в других Лиспах.
  • Предпочитайте ключевые слова цитируемым символам. Ключевые слова выделяются как «постоянные строки» или перечисляемые значения. Ключевые слова в Clojure могут принадлежать пространству имен, поэтому они имеют все преимущества символов. И опять же, нет необходимости указывать ключевые слова, что приятно. Символы в кавычках используются довольно редко в Clojure, если вы не пишете макросы.
  • #'first - это переменная, которая называется "first"; first - это значение переменной, называемой "first", то есть fn. В этом случае (#'first foo) и (first foo) дают один и тот же ответ, но #'first делает дополнительную разыменование каждый раз, когда вы его вызываете. Так что не делайте этого, если вы не хотите, чтобы разыменование происходило снова и снова. Обычно нет необходимости использовать #'.
  • Встроенный map ленив, а ваш - нет. Встроенный map использует преимущества последовательных последовательностей для лучшей производительности, а ваш - нет. Идиоматический код не должен быть ленивым или использовать фрагментированные seqs, но имейте в виду, что встроенные функции имеют некоторое продолжение этой магии. Так что лучше воспользоваться.
  • Вместо (= '() x) идиоматический тест для пустого seq равен (seq x), который возвращает nil, если x пусто. Обратите внимание, что в Clojure (= '() nil) является ложным.
  • Если вам когда-либо понадобится использовать пустой список (что вам редко нужно делать), вам не нужно его цитировать. Просто используйте ().
  • Встроенный map сначала принимает аргумент функции, потому что он принимает несколько аргументов коллекции. Когда функция принимает несколько аргументов, эти аргументы должны идти последними в списке аргументов. Я думаю, что это читается лучше и в другом направлении: «(map f coll): отобразить эту функцию на всю коллекцию».
  • Нет необходимости использовать cond, если у вас есть только два варианта. Вместо этого вы можете использовать if. И если одна из веток в вашем if вернет nil, вы можете использовать when. Хорошо использовать when и if, когда это уместно, потому что они немедленно сообщают читателю о ваших намерениях, тогда как cond может делать что угодно и заставляет читателя читать дальше.

Версия Рафаля Доугирда идиоматична, за исключением того, что я перевернул бы порядок рассуждений. И я бы назвал это так:

user> (mapper first [[:blue :red] [:green :red] [:white :red]])
(:blue :green :white)
4 голосов
/ 11 августа 2010

Ваша версия выглядит хорошо для меня.Обычные имена, которые вы увидите в базе кода clojure, - это 'coll' для коллекций.Я также видел 'xs' в стиле Haskell, я думаю.Вы также можете обратиться к стандартам кодирования библиотеки Clojure в различных соглашениях.

Возвращаясь к примеру: два наблюдения.

  1. Использование: else для 'cond' в качестве условия выхода, вместо стиля Common Lisp 'T'.
  2. Вместо того, чтобы предполагать списки, подумайте о последовательностях.

Имея в виду эти два, если я переписываю ваш код:

user> (defn mapper [coll f]
        (cond
          (not (seq coll)) nil
          :else (conj (mapper (next coll) f)
                      (f (first coll)))))
#'user/mapper
user> (mapper '(1 2 3) #(* % %))
(1 4 9)
user> (mapper [1 2 3] #(* % %))
(1 4 9)

Обратите внимание, что "Con" делает "правильную вещь" какЧто касается коллекций.Он добавляет новый элемент в начало списка, в конец вектора и так далее.Также обратите внимание на использование выражения «следующий» вместо идиом «первый / остальные» в традиционном языке LISP.«next» возвращает последовательность элементов после первого элемента.Таким образом, пустота может быть проверена путем seq'ing для коллекции, которая вернет nil для пустого списка или пустого вектора.Таким образом, он работает для всех коллекций.

4 голосов
/ 11 августа 2010

Полагаю, вы поняли это в основном идиоматично. Собственный Clojure map использует:

(defn mapper [coll f]
 (when-let [s (seq coll)]
    (cons (f (first s)) (mapper (rest s) f))))

Я резко сократил его - оригинал создает ленивую последовательность, имеет дело с несколькими коллекциями, chunked-seqs и т. Д. Кстати - я предполагаю, что вы хотите передать фактическую функцию, а не имя .

coll и f - это идиоматические имена arg для представления коллекций и функций соответственно.

...