Сделайте имя функции квалифицированным для пространства имен в макросе - PullRequest
5 голосов
/ 04 октября 2011

У меня есть набор функций, которые отображаются в и из некоторых кодов, определенных внешней системой:

(defn translate-from-ib-size-tick-field-code [val]
  (condp = val
    0 :bid-size
    3 :ask-size
    5 :last-size
    8 :volume))

(defn translate-to-ib-size-tick-field-code [val]
  (condp = val
    :bid-size 0
    :ask-size 3
    :last-size 5
    :volume 8))

Я хотел бы сделать макрос для удаления дублирования:

#_ (translation-table size-tick-field-code
                      {:bid-size 0
                       :ask-size 3
                       :last-size 5
                       :volume 8})    

Я запустил макрос следующим образом:

(defmacro translation-table [name & vals]
  `(defn ~(symbol (str "translate-to-ib-" name)) [val#]
     (get ~@vals val#)))

Полученное тело функции кажется правильным, но имя функции неверное:

re-actor.conversions> (macroexpand `(translation-table monkey {:a 1 :b 2}))
(def translate-to-ib-re-actor.conversions/monkey 
     (.withMeta (clojure.core/fn translate-to-ib-re-actor.conversions/monkey      
     ([val__10589__auto__] 
        (clojure.core/get {:a 1, :b 2} val__10589__auto__))) (.meta ...

Мне бы хотелось "translate-to-ib- "появляться как часть имени функции, а не как префикс к пространству имен, как оказалось.

Как я могу сделать это с макросами clojure?Если я просто делаю это неправильно и по какой-то причине не могу использовать макросы для этого, пожалуйста, дайте мне знать, но я также хотел бы знать, как создавать такие имена функций, чтобы просто улучшить мое понимание clojure и макросов в целом.,Спасибо!

Ответы [ 2 ]

7 голосов
/ 04 октября 2011

Макрос выпускается в два раза:

1) Вы используете обратную кавычку при цитировании формы, переданной macroexpand, в которой пространство имен квалифицирует символы внутри:

`(translation-table monkey {:a 1 :b 2})
=> (foo.bar/translation-table foo.bar/monkey {:a 1, :b 2})

где foo.bar - это любое пространство имен, в котором вы находитесь.

2) Вы создаете имя элемента defn, используя символ name, который, когда он квалифицирован пространством имен, будет преобразован в "foo.bar/monkey". Вот версия, которая будет работать:

(defmacro translation-table [tname & vals]
  `(defn ~(symbol (str "translate-to-ib-" (name tname))) [val#]
     (get ~@vals val#)))

Обратите внимание, что мы получаем имя tname без части пространства имен, используя функцию name.

Что касается того, является ли макрос здесь правильным решением, вероятно, нет :-) Для простого случая, подобного этому, я мог бы просто использовать карты:

(def translate-from-ib-size-tick-field-code 
  {0 :bid-size
   3 :ask-size
   5 :last-size
   8 :volume})

;; swap keys & vals
(def translate-to-ib-size-tick-field-code
  (zipmap (vals translate-from-ib-size-tick-field-code)
          (keys translate-from-ib-size-tick-field-code)))

(translate-from-ib-size-tick-field-code 0)
=> :bid-size

(translate-to-ib-size-tick-field-code :bid-size)
=> 0

Если скорость важна, проверьте case.

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

Несколько незапрошенных советов по другому вопросу: (get ~@vals val#) крайне подозрительно.Ваш макрос утверждает, что принимает любое количество аргументов, но если он получит больше двух, он просто сделает что-то, что не имеет никакого смысла.Например,

(translation-table metric {:feet :meters} 
                          {:miles :kilometers}
                          {:pounds :kilograms})

, кроме того, что является ужасной таблицей перевода, расширяется до кода, который всегда выдает исключение:

(defn translate-to-ib-metric [val]
  (get {:feet :meters} 
       {:miles :kilometers}
       {:pounds :kilograms}
       val)))

get, конечно, не принимает столько аргументови это не совсем то, что ты имел ввиду.Простейшим решением было бы разрешить только два аргумента:

(defmacro translation-table [name vals] 
  (... (get ~vals val#)))

Но учтите, что это означает, что карта значений воссоздается каждый раз, когда вызывается функция, - проблематично, если это дорого для вычисления или имеет побочные эффекты.Поэтому, если бы я писал это как макрос (хотя см. Ответ Джастина - зачем вам?), Я сделал бы это следующим образом:

(defmacro translation-table [name vals]
  `(let [vals# ~vals]
     (defn ~(symbol (str "translate-to-ib-" name)) [val#]
       (get vals# val#))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...