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

时间:2021-05-24 11:01:07

I come with this:

我来这里:

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

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

But it must be a better way to do this.

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

5 个解决方案

#1


145  

A function can have multiple signatures if the signatures differ in arity. You can use that to supply default values.

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

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

Note that assuming false and nil are both considered non-values, (if (nil? base) 10 base) could be shortened to (if base base 10), or further to (or base 10).

注意,假设false和nil都被认为是非值,(if(nil?base)10 base)可以缩短为(如果基数为10),或者进一步缩短为(或基数为10)。

#2


141  

You can also destructure rest arguments as a map since Clojure 1.2 [ref]. This lets you name and provide defaults for function arguments:

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

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

Now you can call

现在你可以打电话了

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

or

要么

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

You can see this in action here: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (for example)

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

#3


31  

This solution is the closer to the spirit of the original solution, but marginally cleaner

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

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

A similar pattern which can be handy uses or combined with let

一种类似的模式,可以方便使用或与let结合使用

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

While in this case more verbose, it can be useful if you wish to have defaults dependent on other input values. For example, consider the following function:

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

(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

This approach can easily be extended to work with named arguments (as in M. Gilliar's solution) as well:

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

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

Or using even more of a fusion:

或者使用更多的融合:

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

#4


8  

There is another approach you might want to consider: partial functions. These are arguably a more "functional" and more flexible way to specify default values for functions.

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

Start by creating (if necessary) a function that has the parameter(s) that you want to provide as default(s) as the leading parameter(s):

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

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

This is done because Clojure's version of partial lets you provide the "default" values only in the order they appear in the function definition. Once the parameters have been ordered as desired, you can then create a "default" version of the function using the partial function:

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

(partial string->integer 10)

In order to make this function callable multiple times you could put it in a var using def:

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

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

You could also create a "local default" using let:

您还可以使用let创建“本地默认值”:

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

The partial function approach has one key advantage over the others: the consumer of the function can still decide what the default value will be rather than the producer of the function without needing to modify the function definition. This is illustrated in the example with hex where I have decided that the default function decimal is not what I want.

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

Another advantage of this approach is you can assign the default function a different name (decimal, hex, etc) which may be more descriptive and/or a different scope (var, local). The partial function can also be mixed with some of the approaches above if desired:

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

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

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

(Note this is slightly different from Brian's answer as the order of the parameters has been reversed for the reasons given at the top of this response)

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

#5


5  

You might also look into (fnil) https://clojuredocs.org/clojure.core/fnil

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

#1


145  

A function can have multiple signatures if the signatures differ in arity. You can use that to supply default values.

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

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

Note that assuming false and nil are both considered non-values, (if (nil? base) 10 base) could be shortened to (if base base 10), or further to (or base 10).

注意,假设false和nil都被认为是非值,(if(nil?base)10 base)可以缩短为(如果基数为10),或者进一步缩短为(或基数为10)。

#2


141  

You can also destructure rest arguments as a map since Clojure 1.2 [ref]. This lets you name and provide defaults for function arguments:

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

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

Now you can call

现在你可以打电话了

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

or

要么

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

You can see this in action here: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (for example)

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

#3


31  

This solution is the closer to the spirit of the original solution, but marginally cleaner

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

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

A similar pattern which can be handy uses or combined with let

一种类似的模式,可以方便使用或与let结合使用

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

While in this case more verbose, it can be useful if you wish to have defaults dependent on other input values. For example, consider the following function:

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

(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

This approach can easily be extended to work with named arguments (as in M. Gilliar's solution) as well:

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

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

Or using even more of a fusion:

或者使用更多的融合:

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

#4


8  

There is another approach you might want to consider: partial functions. These are arguably a more "functional" and more flexible way to specify default values for functions.

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

Start by creating (if necessary) a function that has the parameter(s) that you want to provide as default(s) as the leading parameter(s):

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

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

This is done because Clojure's version of partial lets you provide the "default" values only in the order they appear in the function definition. Once the parameters have been ordered as desired, you can then create a "default" version of the function using the partial function:

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

(partial string->integer 10)

In order to make this function callable multiple times you could put it in a var using def:

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

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

You could also create a "local default" using let:

您还可以使用let创建“本地默认值”:

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

The partial function approach has one key advantage over the others: the consumer of the function can still decide what the default value will be rather than the producer of the function without needing to modify the function definition. This is illustrated in the example with hex where I have decided that the default function decimal is not what I want.

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

Another advantage of this approach is you can assign the default function a different name (decimal, hex, etc) which may be more descriptive and/or a different scope (var, local). The partial function can also be mixed with some of the approaches above if desired:

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

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

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

(Note this is slightly different from Brian's answer as the order of the parameters has been reversed for the reasons given at the top of this response)

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

#5


5  

You might also look into (fnil) https://clojuredocs.org/clojure.core/fnil

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