在Clojure中,如何编写具有可变数量参数的工厂

时间:2012-02-13 07:36:34

标签: clojure

我想在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 个答案:

答案 0 :(得分:2)

我建议您将其他参数指定为集合:

(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)))

请注意,如果您希望允许调用者提供特定的额外参数,那么替代(或可能是互补的?)方法将是定义变量arity函数,例如:类似的东西:

(defn create-document
  ([language]
    .....handle no argument.....)
  ([language arg]
    .....handle 1 argument.....)
  ([language arg & more-args]
    .....handle more than one argument.....))

答案 1 :(得分:2)

我猜multimethods会产生更干净的代码。使用您写的create-long-documentcreate-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"

通过这种方式,调度逻辑由multimethods API提供,无需编写自己的调度函数。这种方法的好处是可扩展性:支持新语言只需要一个新的defmethod

答案 2 :(得分:1)

我想这可能很简单:

(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))
  )