137

我带着这个:

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

(字符串->整数“10”)
(字符串->整数“FF”16)

但它必须是一个更好的方法来做到这一点。

4

6 回答 6

182

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

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

于 2010-07-08T22:10:18.743 回答
167

从 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 (例如)

于 2011-12-28T20:21:23.057 回答
38

这个解决方案更接近原始解决方案的精神,但稍微干净一些。

(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)))
于 2013-10-01T10:40:50.820 回答
11

您可能还需要考虑另一种方法:函数。这些可以说是一种更“实用”和更灵活的方式来指定函数的默认值。

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

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

这样做是因为 Clojure 的版本partial允许您仅按照它们在函数定义中出现的顺序提供“默认”值。根据需要对参数进行排序后,您可以使用以下partial函数创建函数的“默认”版本:

(partial string->integer 10)

为了使此函数可多次调用,您可以使用以下方法将其放入 var 中def

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

(请注意,这与布赖恩的回答略有不同,因为由于此响应顶部给出的原因,参数的顺序已经颠倒了)

于 2014-07-19T02:20:16.777 回答
7

您还可以查看(fnil) https://clojuredocs.org/clojure.core/fnil

于 2015-09-21T05:17:46.333 回答
0

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

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

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

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

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

轻微的缺点是,在没有选项的情况下调用时,仍然必须提供一个空地图。

位置参数部分(Code Smells 文章)涉及到这一点。

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

于 2020-12-30T19:32:59.813 回答