Clojure: генерация функций из шаблона - PullRequest
6 голосов
/ 18 февраля 2011

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

(defn using-format [format] {:format format})

(defn- parse-date [str format]
  (.parse (java.text.SimpleDateFormat. format) str))

(defn string-to-date
  ([str] 
    (string-to-date str (using-format "yyyy-MM-dd")))
  ([str conversion-params] 
    (parse-date str (:format (merge (using-format "yyyy-MM-dd") conversion-params)))))

Мне нужно уметь называть это так:

(string-to-date "2011-02-17")

(string-to-date "2/17/2011" (using-format "M/d/yyyy"))

(string-to-date "2/17/2011" {})

Третий случай несколько проблематичен: карта не обязательно содержит ключ :format, который критичен для функции. Вот почему merge со значением по умолчанию.

Мне нужно иметь дюжину похожих функций для преобразования между всеми другими типами. Есть ли более элегантный способ, при котором мне не нужно копировать-вставлять, использовать merge и т. Д. В каждой функции?

В идеале, искать что-то вроде этого (макрос?):

(defn string-to-date
  (wrap
     (fn [str conversion-params] 
       (parse-date str (:format conversion-params))) ; implementation
     {:format "yyyy-MM-dd"})) ; default conversion-params

... которая создаст перегруженную функцию (унарную и двоичную), причем двоичная функция имеет merge, как в первом примере.

Ответы [ 3 ]

5 голосов
/ 18 февраля 2011

Итак, чтобы определить это более строго, вы хотите создать макрос, который создает функции конвертера.Функция конвертера - это функция с двумя аргументами, одним аргументом и двумя аргументами.Первый аргумент функции преобразователя - это объект, который нужно преобразовать.Второй аргумент - это карта параметров, которая как-то повлияет на преобразование (например, строка формата в вашем примере.)

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

Давайте назовем этот макрос def-converter.Преобразователь Def будет принимать три аргумента, первый - имя создаваемой функции.Второй - это анонимная функция с двумя аргументами, которая реализует двухартериальный преобразователь без объединения параметров по умолчанию.Третьим аргументом является карта параметров по умолчанию.

Что-то вроде этого будет работать:

(defmacro def-converter [converter-name converter-fn default-params]
  (defn ~converter-name
   ([to-convert#] 
    (let [default-params# ~(eval default-params)] 
      (~converter-fn to-convert# default-params#)))
   ([to-convert# params#] 
    (let [default-params# ~(eval default-params)]
      (~converter-fn to-convert# (merge default-params# params#))))))

Тогда вы можете использовать его так:

(def-converter 
  string-to-date  
  (fn [to-convert conversion-params] 
    (parse-date to-convert conversion-params))
  (using-format "yyyy-MM-dd"))

Но вы должнывнесите изменения в одну из своих вспомогательных функций:

(defn- parse-date [str params] 
  (.parse (java.text.SimpleDateFormat. (:format params)) str))

Это потому, что макрос должен быть достаточно общим, чтобы обрабатывать произвольные карты параметров, поэтому мы не можем рассчитывать.Вероятно, есть способы обойти это, но я не могу придумать один случай, который не сложнее, чем просто перенести его на вспомогательную функцию (или анонимную функцию, которую нужно передать в def-конвертер).

2 голосов
/ 18 февраля 2011

clojure.contrib.def / defnk удобно, если вам нужны функции с аргументами ключевых слов по умолчанию:

  (use 'clojure.contrib.def)
  ...

  (defnk string-to-date [str :format "yyyy-MM-dd"]
    (parse-date str format))

  (string-to-date "2011-02-17")

  (string-to-date "2/17/2011" :format "M/d/yyyy")
1 голос
/ 18 февраля 2011

Для справки, вот что я понял позже ночью:

(defmacro defconvert [name f default]
  `(defn ~name
     ([v#] (~name v# ~default))
     ([v# conversion-params#] (~f v# (merge ~default conversion-params#)))))

Кажется, это работает и генерирует именно то определение, которое у меня было там.Можно ли это сделать с помощью defnk или другого встроенного механизма, имеющего карту значений по умолчанию и допускающего переопределение некоторых, но не обязательно всех?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...