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


127

我来这个:

(defn字符串->整数[str&[base]]
  (Integer / parseInt str(如果(nil?base)10 base)))

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

但这一定是更好的方法。

Answers:


170

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

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


3
我认为这将是更好的为第二行说(recur s 10),使用recur代替重复的功能名称string->integer。这将使将来更容易重命名该功能。有谁知道recur在这些情况下不使用的任何理由?
罗里·奥肯

10
看起来recur只能在相同的工具上使用。如果您尝试在上方java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
重播

遇到同样的问题。调用函数本身(即(string->integer s 10))是否有意义?
Kurt Mueller

155

rest从Clojure 1.2 [ ref ] 开始,您还可以将参数分解为映射。这使您可以命名并提供函数参数的默认值:

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


1
非常容易理解,如果来自Python背景:)
2012年

1
这比公认的答案更容易理解...这是公认的“ Clojurian”方式吗?请考虑添加到此文档中。
Droogans

1
我在非官方样式指南中添加了一个问题,以帮助解决此问题。
Droogans

1
这个答案比公认的答案更有效地捕捉了“正确”的方法,尽管两者都可以正常工作。(当然,Lisp语言的强大之处在于,通常有许多不同的方法可以完成相同的基本操作)
johnbakers 2014年

2
这对我来说似乎有点罗word,我很难记住它一会儿,所以我创建了一个稍微不那么冗长的宏
akbiggs 2014年

33

此解决方案更接近于原始解决方案精神,但略为清洁

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

如果不需要默认值依赖于其他默认值(或者即使您需要),则上述Matthew的解决方案还允许为不同的变量提供多个默认值。它比使用常规方法干净得多or
johnbakers 2014年

我是Clojure新手,所以OpenLearner也许是正确的,但这是Matthew上面解决方案的有趣替代方案。我很高兴知道这一点,无论我是否最终决定使用它。
GlenPeterson 2014年

or从不同的:or,因为or不知道的差异nilfalse
茹连

@XiangruLian您是说在使用:or时,如果传递false,它将知道使用false而不是默认值?当with或传递false时会使用默认值,而不是false本身?
Didier A.

8

您可能要考虑另一种方法:部分函数。可以说,这是一种为功能指定默认值的更“实用”和更灵活的方式。

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

(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的回答略有不同,因为出于此响应顶部给出的原因,参数的顺序已颠倒)


1
这不是问题的要求;这很有趣。
Zaz 2015年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.