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

我带来了这个:

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


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

但肯定有更好的办法。

62284 次浏览

如果签名的种类不同,函数可以有多个签名。您可以使用它来提供默认值。

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

您还可以从 Clojure 1.2[ 裁判]开始将 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(例如)

这种解决方案更接近于 原始解决方案的精神,但略微清洁

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

一个 相似的模式可以方便地使用 orlet结合

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

您可能需要考虑另一种方法: 不完整函数。可以说,这是一种更“功能性”和更灵活的方式来指定函数的默认值。

首先创建一个函数(如果有必要) ,其中包含要作为默认参数提供的参数作为前导参数:

(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

部分函数方法与其他方法相比有一个关键优势: 函数的 消费者仍然可以决定默认值是什么,而不是函数 不需要修改函数定义制片人。在使用 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 的答案略有不同,因为参数的顺序已经被颠倒,原因在这个响应的顶部给出)

Matthew 建议的一种非常类似的方法是不使用 & rest 参数,而是要求调用方提供灵活(和可选)键的单个额外 map 参数。

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


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


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

这样做的一个好处是,这些选项是单个参数映射,调用方不必非常小心地传递偶数个额外的参数。此外,短绒可以做得更好,这种风格。编辑器还应该更好地处理映射键-值表格对齐(如果你喜欢的话) ,而不是每行一对参数。

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

这在本 立场论点部分(代码嗅觉文章)中有所涉及。

更新: 现在 Clojure 支持1.11版本的 传入一个可选参数的映射(使用 &)。