Макрос Clojure для создания синонима функции - PullRequest
6 голосов
/ 22 августа 2009

Вероятно, это легко для любого, кто действительно знает, как писать макросы в любом Лиспе. Я хочу иметь возможность определять синонимы для имен функций. Я делал хакерские копии и вставлял core.clj, чтобы сделать это, но я не хочу быть таким придурком вечно! Кажется очевидным, что макрос, который переписывает вызов функции synoym в вызов исходной функции, является правильным способом сделать это.

Ответы [ 2 ]

13 голосов
/ 22 августа 2009

Если я понимаю ваш вопрос, есть более простой способ: перевести новый символ в старую функцию.

user=> (def foo +)
#'user/foo 
user=> (foo 1 2) 
3

Производительность def также превосходит макроподход:

(defmacro foo2 [& args]
  `(+ ~@args))

foo2 фактически является псевдонимом для + и ведет себя точно так же (перезаписывается как +) за исключением ограничений, накладываемых на использование макросов, где должно быть возвращено значение.

Если вы хотите, чтобы поведение «псевдонима» было точно таким же, как и в исходной функции (также может вызываться в тех же контекстах), вам нужно использовать def для переименования функции.

user=> (def foo +)

user=> (defn foo1 [& args]
         `(+ ~@args))

user=> (defmacro foo2 [& args]
         `(+ ~@args))

user=> (time (dotimes [n 1000000] (foo 1 n)))
"Elapsed time: 37.317 msecs"

user=> (time (dotimes [n 1000000] (foo1 1 n)))
"Elapsed time: 292.767 msecs"

user=> (time (dotimes [n 1000000] (foo2 1 n)))
"Elapsed time: 46.921 msecs"
2 голосов
/ 23 марта 2014

Макросы теперь быстрее

Я приступил к (безусловно, глупой) задаче переименования некоторых основных функций Clojure в одном из моих проектов. Я получаю от этого огромное удовольствие (fn становится λ, loop становится и т. Д.), Но мне показалось, что мне очень интересно работать. Отличный ответ Грега от пятилетней давности теперь, я думаю, частично неверен. Я использую Clojure 1.5.1.

Для начала:

user=> (defn foo1 [& args] `(+ ~@args))
#'user/foo1
user=> (foo1 1 2 3 4)
(clojure.core/+ 1 2 3 4)

Это определенно не то, что вы хотите. Более того, похоже, что макросы сейчас, безусловно, самый быстрый вариант. Я продублировал эксперименты Грега и получил совсем другие результаты. Время, которое вы видите ниже, является лучшим из десяти пробежек:

user=> (def foo +)
#'user/foo
user=> (defn foo1 [& args] (apply + args))
#'user/foo1
user=> (defmacro foo2 [& args] `(+ ~@args))
#'user/foo2
user=> (time (dotimes [n 1000000] (+ 1 n)))
"Elapsed time: 53.401812 msecs"
user=> (time (dotimes [n 1000000] (foo 1 n)))
"Elapsed time: 135.675486 msecs"
user=> (time (dotimes [n 1000000] (foo1 1 n)))
"Elapsed time: 494.770352 msecs"
user=> (time (dotimes [n 1000000] (foo2 1 n)))
"Elapsed time: 53.509264 msecs"

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

user=> (defmacro α [& body] `(reduce ~@body))
#'user/α
user=> (defn β [& body] (apply reduce body))
#'user/β
user=> (def γ reduce)
#'user/γ
user=> (time (dotimes [n 10000] (reduce + (range n))))
"Elapsed time: 5466.920266 msecs"
user=> (time (dotimes [n 10000] (α + (range n))))
"Elapsed time: 5474.532622 msecs"
user=> (time (dotimes [n 10000] (β + (range n))))
"Elapsed time: 5491.337517 msecs"
user=> (time (dotimes [n 10000] (γ + (range n))))
"Elapsed time: 5456.271967 msecs"

Наконец, то, что вы ищете, может быть defalias из clojure.contrib.def. У меня нет опыта с этим.

...