【问题标题】:How to create default value for function argument in Clojure如何在 Clojure 中为函数参数创建默认值
【发布时间】:2011-03-13 14:36:18
【问题描述】:

我来了:

(defn string->integer [str & [base]] (Integer/parseInt str (if (nil?base) 10 base))) (字符串->整数“10”) (字符串->整数“FF”16)

但这一定是更好的方法。

【问题讨论】:

    标签: function clojure default-value optional-parameters


    【解决方案1】:

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

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

    【讨论】:

    • 我认为第二行写(recur s 10)会更好,使用recur而不是重复函数名string->integer。这将使将来更容易重命名该函数。有谁知道在这些情况下不使用recur 的任何理由?
    • 看起来recur 只适用于相同的arity。如果您在上面尝试了 recur,例如:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
    • 遇到了同样的问题。让函数自己调用是否有意义(即(string->integer s 10))?
    【解决方案2】:

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

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

    【讨论】:

    • 如果有 Python 背景就很容易理解 :)
    • 这比公认的答案更容易理解……这是公认的"Clojurian" 方式吗?请考虑添加到此文档中。
    • 我有 added an issue 到非官方风格指南来帮助解决这个问题。
    • 这个答案比公认的答案更有效地捕捉到了“正确”的方法,尽管两者都可以正常工作。 (当然,Lisp 语言的强大之处在于,通常有许多不同的方法来做同样的基本事情)
    • 这对我来说有点罗嗦,我有一段时间记不住了,所以我创建了a slightly less verbose macro
    【解决方案3】:

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

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

    一个相似的模式可以方便地使用orlet结合

    (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 要干净得多
    • 我是 Clojure 菜鸟,所以也许 OpenLearner 是对的,但这是 Matthew 上述解决方案的有趣替代方案。无论我最终决定是否使用它,我都很高兴知道这一点。
    • or:or 不同,因为or 不知道nilfalse 的区别。
    • @XiangruLian 你是说在使用时:或者,如果你通过false,它会知道使用false而不是默认值? while with or 它会在传递 false 而不是 false 本身时使用默认值?
    【解决方案4】:

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

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

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

    【讨论】:

    • 这不是问题要问的;不过这很有趣。
    【解决方案5】:

    【讨论】:

      【解决方案6】:

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

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

      这样做的好处是选项是单个参数映射,调用者不必非常小心地传递偶数个额外参数。另外,linter 可以用这种风格做得更好。与每行一对 args 相比,编辑器还应该更好地使用 map 键值表格对齐(如果你喜欢的话)。

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

      Positional Arguments section(Code Smells 文章)中提到了这一点。

      更新:Clojure 1.11 现在支持passing in a map for optional args(使用&)。

      【讨论】:

      • 要求是可选的。也许添加& 但你说过这就是区别。
      猜你喜欢
      • 2021-06-10
      • 1970-01-01
      • 1970-01-01
      • 2013-01-01
      • 2011-12-06
      • 2019-09-11
      • 2012-08-01
      • 2012-05-22
      • 2010-10-03
      相关资源
      最近更新 更多