В Clojure, как написать фабрику с переменным числом аргументов - PullRequest
1 голос
/ 13 февраля 2012

Я хотел бы создать фабрику в clojure, где количество аргументов создателя изменяется во время выполнения.

Например:

(defn create-long-document [direction]
  (str "long document " direction))

(defn create-short-document[]
  "short document")

(def creator-map {
 :english create-short-document
 :hebrew  create-long-document
})
(def additional-arg-map {
 :english nil
 :hebrew "rtl"
})

 (defn create-document [language]
  (let [creator (language creator-map) arg (language additional-arg-map)]
    (if arg (creator arg) (creator))))


(println (create-document :hebrew)); long document rtl
(println (create-document :english)); short document

Я ищу элегантный способ переписать тело create-document. Я хочу избавиться от if. Может быть, введя умный макрос?

Пожалуйста, поделитесь своими идеями.

Ответы [ 3 ]

2 голосов
/ 13 февраля 2012

Полагаю, мультиметоды приведут к более чистому коду.Используя те же определения для create-long-document и create-short-document, которые вы написали:

; change identity to something more sophisticated 
; if you want sanity checks on input:
(defmulti create-document identity)

(defmethod create-document :default [lang] "language not supported")

(defmethod create-document :english [lang] (create-short-document))

(defmethod create-document :hebrew [lang] (create-long-document "rtl"))

Тогда create-document будет работать должным образом:

user=> (create-document :hebrew)
"long document rtl"
user=> (create-document :english)
"short document"
user=> (create-document :italian)
"language not supported"

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

2 голосов
/ 13 февраля 2012

Я бы предложил указать ваши дополнительные аргументы в виде коллекций:

(def additional-arg-map {
  :english []
  :hebrew  ["rtl"]})

Затем вы можете использовать apply в вашей функции создания документа, что-то вроде:

(defn create-document [language]
  (let [creator (creator-map language)
        args    (additional-arg-map language)]
   (apply creator args)))

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

(defn create-document
  ([language]
    .....handle no argument.....)
  ([language arg]
    .....handle 1 argument.....)
  ([language arg & more-args]
    .....handle more than one argument.....))
1 голос
/ 13 февраля 2012

Полагаю, это может быть так просто:

(defn create-long-document [direction]
  (str "long document " direction))

(defn create-short-document[]
  "short document")

(def creator-map {
 :english #(create-short-document)
 :hebrew  #(create-long-document "rtl")
})

(defn create-document [language]
    ((creator-map language))
  )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...