简体   繁体   English

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

[英]How to create default value for function argument in Clojure

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.但它必须是一个更好的方法来做到这一点。

A function can have multiple signatures if the signatures differ in arity.如果签名的数量不同,一个函数可以有多个签名。 You can use that to supply default values.您可以使用它来提供默认值。

(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) .请注意,假设falsenil都被视为非值, (if (nil? base) 10 base)可以缩短为(if base base 10) ,或者进一步缩短为(or base 10)

You can also destructure rest arguments as a map since Clojure 1.2 [ ref ].自 Clojure 1.2 [ ref ] 起,您还可以将rest参数解构为映射。 This lets you name and provide defaults for function arguments:这使您可以为函数参数命名并提供默认值:

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

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一个类似的模式,可以方便地使用orlet结合使用

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

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.这样做是因为 Clojure 版本的partial允许您仅按照它们在函数定义中出现的顺序提供“默认”值。 Once the parameters have been ordered as desired, you can then create a "default" version of the function using the partial function:根据需要对参数进行排序后,您可以使用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.这在带有hex示例中进行了说明,其中我决定默认函数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).这种方法的另一个优点是您可以为默认函数分配不同的名称(十进制、十六进制等),这可能更具描述性和/或不同的范围(var、local)。 The partial function can also be mixed with some of the approaches above if desired:如果需要,部分函数也可以与上述一些方法混合使用:

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

A very similar approach to Matthew's suggestion is to not do the & rest args, but require that callers provide the single extra map argument of flexible (and optional) keys.与 Matthew 的建议非常相似的方法是不执行& rest args,而是要求调用者提供灵活(和可选)键的单个额外 map 参数。

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

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

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

There is a benefit to this in that the options are a single argument map and the caller doesn't have to be so careful about passing an even number of extra args.这样做的好处是选项是单个参数映射,调用者不必非常小心传递偶数个额外参数。 Plus, linters can do better with this style.另外,linters 可以用这种风格做得更好。 Editors should also do better with map key-value tabular alignment (if you're into that) than pairs of args per line.与每行成对的 args 相比,编辑器还应该在地图键值表格对齐方面做得更好(如果您愿意的话)。

The slight down-side is that when called with no options, an empty map must still be provided.稍有不利的一面是,在没有选项的情况下调用时,仍必须提供空映射。

This is touched on in this Positional Arguments section (Code Smells article).这在这个位置参数部分(代码气味文章)中有所涉及。

UPDATE: There is now support in Clojure 1.11 for passing in a map for optional args (using & ).更新:现在 Clojure 1.11 支持传递可选参数的映射(使用& )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM