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

jcu*_*bic 121 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)

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

Bri*_*per 161

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

(defn string->integer 
  ([s] (string->integer s 10))
  ([s base] (Integer/parseInt s base)))
Run Code Online (Sandbox Code Playgroud)

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

  • 看起来`recur`只适用于同一个arity.如果你尝试重复上述,例如:`java.lang.IllegalArgumentException:不匹配的参数计数重复,预期:1 args,got:2,编译:` (10认同)
  • 我认为第二行更好地说'(recur s 10)`,使用[`recur`](http://grimoire.arrdem.com/1.6.0/clojure.core/recur/)而不是重复函数名称`string-> integer`.这样可以更容易在将来重命名该功能.有谁知道在这些情况下不使用`recur`的任何理由? (3认同)
  • 遇到了同样的问题。让函数调用本身有意义吗(即“(string->integer s 10)”)? (2认同)

Mat*_*ard 149

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

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

现在你可以打电话了

(string->integer "11")
=> 11
Run Code Online (Sandbox Code Playgroud)

要么

(string->integer "11" :base 8)
=> 9
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到这个:https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj(例如)

  • 这对我来说有点罗嗦,我很难记住它一段时间,所以我创建了[一个稍微冗长的宏](https://gist.github.com/akbiggs/86f5bd96b7303b5c3882). (2认同)

met*_*ous 33

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

(defn string->integer [str & [base]]
  (Integer/parseInt str (or base 10)))
Run Code Online (Sandbox Code Playgroud)

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

(defn string->integer [str & [base]]
  (let [base (or base 10)]
    (Integer/parseInt str base)))
Run Code Online (Sandbox Code Playgroud)

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

(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
Run Code Online (Sandbox Code Playgroud)

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

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

或者使用更多的融合:

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


opt*_*evo 8

您可能还需要考虑另一种方法:部分功能.这些可以说是一种更"功能",更灵活的方式来指定函数的默认值.

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

(defn string->integer [base str]
  (Integer/parseInt str base))
Run Code Online (Sandbox Code Playgroud)

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

(partial string->integer 10)
Run Code Online (Sandbox Code Playgroud)

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

(def decimal (partial string->integer 10))
(decimal "10")
;10
Run Code Online (Sandbox Code Playgroud)

您还可以使用let以下方法创建"本地默认值" :

(let [hex (partial string->integer 16)]
  (* (hex "FF") (hex "AA")))
;43350
Run Code Online (Sandbox Code Playgroud)

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

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

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

(def hex (partial string->integer 16))
Run Code Online (Sandbox Code Playgroud)

(请注意,这与Brian的答案略有不同,因为参数的顺序已被颠倒,原因在此响应的顶部给出)