如何在Clojure中为函数参数创建默认值

时间:2010-07-08 22:01:37

标签: function clojure default-value optional-parameters

我来这里:

(defn string->integer [str & [base]]
  (Integer/parseInt str (if (nil? base) 10 base)))

(string->integer "10")
(string->integer "FF" 16)

但它必须是更好的方法。

6 个答案:

答案 0 :(得分:156)

如果签名在arity中不同,则函数可以具有多个签名。您可以使用它来提供默认值。

(defn string->integer 
  ([s] (string->integer s 10))
  ([s base] (Integer/parseInt s base)))

请注意,假设falsenil都被视为非值,(if (nil? base) 10 base)可以缩短为(if base base 10),或者缩短为(or base 10)

答案 1 :(得分:148)

自Clojure 1.2 [ref]以来,您还可以将rest参数解构为地图。这允许您为函数参数命名并提供默认值:

(defn string->integer [s & {:keys [base] :or {base 10}}]
    (Integer/parseInt s base))

现在你可以打电话了

(string->integer "11")
=> 11

(string->integer "11" :base 8)
=> 9

您可以在此处查看此操作:https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj(例如)

答案 2 :(得分:32)

此解决方案更接近原始解决方案的精神,但略微清晰

(defn string->integer [str & [base]]
  (Integer/parseInt str (or base 10)))

类似模式,可以方便使用or结合let

(defn string->integer [str & [base]]
  (let [base (or base 10)]
    (Integer/parseInt str base)))

虽然在这种情况下更详细,但如果您希望默认值依赖于其他输入值,则会很有用。例如,请考虑以下函数:

(defn exemplar [a & [b c]]
  (let [b (or b 5)
        c (or c (* 7 b))]
    ;; or whatever yer actual code might be...
    (println a b c)))

(exemplar 3) => 3 5 35

这种方法可以很容易地扩展到使用命名参数(如在M. Gilliar的解决方案中):

(defn exemplar [a & {:keys [b c]}]
  (let [b (or b 5)
        c (or c (* 7 b))]
    (println a b c)))

或者使用更多的融合:

(defn exemplar [a & {:keys [b c] :or {b 5}}]
  (let [c (or c (* 7 b))]
    (println a b c)))

答案 3 :(得分:8)

您可能需要考虑另一种方法:partial函数。这些可以说是更具功能性的#34;以及更灵活的方式来指定函数的默认值。

首先创建(如有必要)一个函数,该函数具有您要提供的参数作为默认值作为主要参数:

(defn string->integer [base str]
  (Integer/parseInt str base))

这样做是因为Clojure的partial版本允许您提供"默认"值仅按它们在函数定义中出现的顺序排列。一旦根据需要订购了参数,您就可以创建一个"默认"使用partial函数的函数版本:

(partial string->integer 10)

为了使这个函数可以多次调用,你可以使用def将它放在var中:

(def decimal (partial string->integer 10))
(decimal "10")
;10

您还可以创建一个"本地默认"使用let

(let [hex (partial string->integer 16)]
  (* (hex "FF") (hex "AA")))
;43350

部分函数方法比其他方法有一个关键优势:函数的使用者仍然可以决定默认值是什么,而不是函数的 producer 无需修改功能定义。在hex的示例中说明了这一点,我已经确定默认函数decimal不是我想要的。

此方法的另一个优点是您可以为默认函数指定一个不同的名称(十进制,十六进制等),这可能更具描述性和/或不同的范围(var,local)。如果需要,部分函数也可以与上面的一些方法混合使用:

(defn string->integer 
  ([s] (string->integer s 10))
  ([base s] (Integer/parseInt s base)))

(def hex (partial string->integer 16))

(请注意,这与Brian的回答略有不同,因为参数的顺序已被反转,原因在于此回复的顶部)

答案 4 :(得分:5)

答案 5 :(得分:0)

与 Matthew 的建议非常相似的方法是不执行 & 其余参数,而是要求调用者提供灵活(和可选)键的单个额外映射参数。

(defn string->integer [s {:keys [base] :or {base 10}}]
    (Integer/parseInt s base))

(string->integer "11" {:base 8})
=> 9

(string->integer "11" {})
=> 11

这样做的好处是,选项是单个参数映射,调用者不必非常小心传递偶数个额外参数。另外,linters 可以用这种风格做得更好。与每行成对的 args 相比,编辑器还应该在地图键值表格对齐方面做得更好(如果您愿意的话)。

稍有不利的一面是,在没有选项的情况下调用时,仍必须提供空映射。

这在 Positional Arguments section(代码异味文章)中有所涉及。

更新: Clojure 1.11 现在支持 passing in a map for optional args(使用 &)。