【问题标题】:Setting default values in clojure defn在 clojure defn 中设置默认值
【发布时间】:2019-04-26 14:32:00
【问题描述】:

我是 clojure 的超级新手,我正在尝试在 clojure 函数中为 args 设置错误值。这是我目前所拥有的:

(defn get-user-projects-by-user-id
([db-conn userid] (get-user-projects-by-user-id db-conn userid (str "asc") nil 10 10))
([db-conn userid sort filters offset size]
      ;;rest of the function

我想要完成的是,每当函数没有接收到排序、过滤器、偏移量或大小,或者作为 nil/false 时,它​​们分别获得“asc”、nil、10 和 10 的默认值.

但是,只要我不将任何这些参数发送到 get-user-projects-by-user-id,(println 排序过滤器偏移大小)就是 nil nil nil nil。

如何将它们设置为我想要的默认值?

【问题讨论】:

    标签: clojure functional-programming


    【解决方案1】:

    有几种方法可以提供可选参数及其默认值。选择哪一个取决于您希望如何为函数调用提供这些可选参数。

    严格的串行参数

    您的参数已经按设置它们的可能性递减排序。您希望能够像这样调用函数(假设 println 函数体并忽略 nil 返回值):

    user> (get-user-projects-by-user-id :conn :uid)
    [:conn :uid asc nil 10 10]
    user> (get-user-projects-by-user-id :conn :uid :sort)
    [:conn :uid :sort nil 10 10]
    user> (get-user-projects-by-user-id :conn :uid :sort "filter")
    [:conn :uid :sort filter 10 10]
    user> (get-user-projects-by-user-id :conn :uid :sort "filter" 9)
    [:conn :uid :sort filter 9 10]
    

    为此,您可以按照已经开始的方式通过 arity 重载函数:

    (defn get-user-projects-by-user-id
      ([db-conn userid]
       (get-user-projects-by-user-id db-conn userid
                                     (str "asc")
                                     nil
                                     10
                                     10))
      ([db-conn userid sort]
       (get-user-projects-by-user-id db-conn userid
                                     sort
                                     nil
                                     10
                                     10))
      ([db-conn userid sort filters]
       (get-user-projects-by-user-id db-conn userid
                                     sort
                                     filters
                                     10
                                     10))
      ([db-conn userid sort filters offset]
       (get-user-projects-by-user-id db-conn userid
                                     sort
                                     filters
                                     offset
                                     10))
      ([db-conn userid sort filters offset size]
       (println [db-conn userid sort filters offset size])))
    

    函数定义有点繁琐,重构时必须注意保持默认值正确。

    作为地图的可选参数

    您可以在参数向量中使用 destructuring 来允许传递带有额外参数的映射。这允许以任何顺序传递它们:您可以覆盖 offset 而不必传递 sort 和其他人:

    (defn get-user-projects-by-user-id-extra-map
      [db-conn userid & [{:keys [sort filters offset size]
                          :or   {sort    "asc"
                                 filters nil
                                 offset  10
                                 size    10}}]]
      (println [db-conn userid sort filters offset size]))
    

    你可以这样使用它:

    user> (get-user-projects-by-user-id-extra-map :conn :uid {:offset 9})
    [:conn :uid asc nil 9 10]
    

    成对的可选参数

    如果您稍微更改解构(注意缺少的[]),它允许您将可选参数作为键值对传递而无需映射。当您的函数调用都是显式的时,这通常更容易使用,而当您 apply 以编程方式收集额外参数的函数时,前一个选项通常更容易。

    (defn get-user-projects-by-user-id-pairs
      [db-conn userid & {:keys [sort filters offset size]
                         :or   {sort    "asc"
                                filters nil
                                offset  10
                                size    10}}]
       (println [db-conn userid sort filters offset size]))
    

    使用它(注意缺少{}):

    user> (get-user-projects-by-user-id-pairs :conn :uid :offset 9)
    [:conn :uid asc nil 9 10]
    

    【讨论】:

      【解决方案2】:

      这里有3种我能想到的方法:

      你的版本:

      (defn get-user-projects-by-user-id
        ([db-conn userid] (get-user-projects-by-user-id db-conn userid "asc" nil 10 10))
        ([db-conn userid sort filters offset size]
         (println "arguments: sort:" sort "filters:" filters "offset:" offset "size:" size)))
      
      ;; (get-user-projects-by-user-id 'db 'userid)
      ;; => arguments: sort asc , filters nil , offset 10 , size 10
      

      另一种方式是传递一个 opts 参数,该参数是一个包含您想要的选项的映射,您可以使用destructuring 以方便的方式提供默认值:

      (defn get-user-projects-by-user-id-with-opts
        ([db-conn userid]
         (get-user-projects-by-user-id-with-opts db-conn userid nil))
        ([db-conn userid {:keys [sort filters offset size]
                          :or   {sort "asc" offset 10 size 10}}]
         (println "arguments: sort:" sort "filters:" filters "offset:" offset "size:" size)))
      
      ;; (get-user-projects-by-user-id-with-opts 'db 'userid)
      ;; => arguments: sort: asc filters: nil offset: 10 size: 10
      
      ;; (get-user-projects-by-user-id-with-opts 'db 'userid {:sort "desc" :offset 20})
      ;; => arguments: sort: desc filters: nil offset: 20 size: 10
      

      最后,在旧库中有时会使用第三种方法,即向函数传递任意数量的参数,然后在其他参数之后获取可选参数(我猜这有点像 Python):

      (defn get-user-projects-by-user-id-with-varargs
        [db-conn userid & args]
        (let [arg-map (apply hash-map args)
              {:keys [sort filters offset size]
               :or   {sort "asc" offset 10 size 10}} arg-map]
          (println "arguments: sort:" sort "filters:" filters "offset:" offset "size:" size)))
      
      ;; (get-user-projects-by-user-id-with-varargs 'db 'userid)
      ;; => arguments: sort: asc filters: nil offset: 10 size: 10
      
      ;; (get-user-projects-by-user-id-with-varargs 'db 'userid :sort "desc" :offset 20)
      ;; => arguments: sort: desc filters: nil offset: 20 size: 10
      

      【讨论】:

      • 虽然您的 varargs 方法有效,但我认为扁平化选项更惯用如下:(defn get-user-projects-by-user-id-with-flat-opts [db-conn userid & {:keys [sort filters offset size] :or {sort "asc" offset 10 size 10}}] (println "arguments: sort:" sort "filters:" filters "offset:" offset "size:" size))) 请注意解构前的“&”。
      • 谢谢。第一种方式仍然打印 nil nil nil nil。但第二种方法有效!
      • 实际上,对于第二种方式,它们的值是设置的。但是当我实际将参数发送给函数时,它们仍然是默认值。我以“desc”的形式发送,但我的 println 排序仍然打印我的默认值 asc。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-28
      • 2010-11-14
      相关资源
      最近更新 更多