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

发布于 2024-09-08 21:36:40 字数 246 浏览 6 评论 0原文

我的想法是:

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

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

但这一定是更好的方法。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

伴梦长久 2024-09-15 21:36:40

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

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

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

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

心的位置 2024-09-15 21:36:40

从 Clojure 1.2 开始,您还可以将 rest 参数解构为映射 [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 (例如)

You can also destructure rest arguments as a map since Clojure 1.2 [ref]. 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)

寄居者 2024-09-15 21:36:40

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

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

一点类似模式,可以方便地使用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)))

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

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

(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)))
风情万种。 2024-09-15 21:36:40

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

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

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

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

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:

(partial string->integer 10)

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

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

You could also create a "local default" using 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.

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:

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

佼人 2024-09-15 21:36:40

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

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

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

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

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

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

位置参数部分(Code Smells 文章)对此进行了讨论。

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

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.

(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. Editors should also do better with map key-value tabular alignment (if you're into that) than pairs of args per line.

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 &).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文