Clojure:不止一个变量重载,更惯用的方法吗?

Clojure:不止一个变量重载,更惯用的方法吗?,clojure,variadic-functions,Clojure,Variadic Functions,一段时间以来,我一直在问自己,是否有一种方法可以定义一个具有多个变量重载的函数。 这是我编写的一个示例函数(我知道没有异常管理,而且可能有更好的编码方法-实际上我没有调试它-但我只关注变量方面): 如您所见,我们可以选择传递一个头。我把它放在可选键上,但我更希望在第一个实例中有类似的内容(即使这个算术映射对我来说没问题): 当然,它不起作用,因为我们不能有超过1个变量重载。我更喜欢它,只是因为它对最终用户来说更清晰 我想了两件事: 使用带有apply…的第二个私有函数,但它不能解决第一个isn

一段时间以来,我一直在问自己,是否有一种方法可以定义一个具有多个变量重载的函数。 这是我编写的一个示例函数(我知道没有异常管理,而且可能有更好的编码方法-实际上我没有调试它-但我只关注变量方面):

如您所见,我们可以选择传递一个头。我把它放在可选键上,但我更希望在第一个实例中有类似的内容(即使这个算术映射对我来说没问题):

当然,它不起作用,因为我们不能有超过1个变量重载。我更喜欢它,只是因为它对最终用户来说更清晰

我想了两件事:

  • 使用带有apply…的第二个私有函数,但它不能解决第一个isntance中的问题,因为我需要两种可能的输入样式
  • 我查看了
    defmulti
    ,但我发现它对每个子方法都采用相同的算术
当然,我也可以在第一个参数中将函数分成两个或两种情况(类型为[vector map]的向量将意味着用户已传递未格式化的数据+标头),但对用户来说情况更糟。 我真的想提供这些输入的可能性

在clojure函数中是否有我没有注意到的东西,或者是我们无法解决的更深层次的问题


谢谢

我想,将所有关键字arg保存在一个单独的映射中是最好的解决方案。。但如果您真的需要(我认为不是这样),还有一种相对流行的方法:您可以将一个变量签名与
arglist
元数据结合使用:

user> (defn parse
        {:arglists '([data header? path & {:keys [sep dec newline]
                                           :or {sep :aaa
                                                dec :bbb
                                                newline :ccc}}])}
        [data header-or-path & args]
        (let [[header path {:keys [sep dec newline]
                            :or {sep :aaa dec :bbb newline :ccc}}]
              (if (even? (count args))
                [nil header-or-path args]
                [header-or-path (first args) (rest args)])]
          (println data header path sep dec newline)))
#'user/parse

user> (parse 1 2)
1 nil 2 :aaa :bbb :ccc
nil

user> (parse 1 2 3)
1 2 3 :aaa :bbb :ccc
nil

user> (parse 1 2 :sep :a :dec :b)
1 nil 2 :a :b :ccc
nil

user> (parse 1 2 3 :sep :a :dec :b)
1 2 3 :a :b :ccc
nil
您(或您库的用户)使用的ide将显示
:arglists
中的签名,忽略真实签名:

user/parse
 [data header? path & {:keys [sep dec newline], :or {sep :aaa, dec :bbb, newline :ccc}}]
  Not documented.
user/parse is defined in *cider-repl localhost*.

但是再次强调:这太冗长了,很难维护(由于代码重复),所以您应该明智地使用它(我的意思是没有人应该这么做)

您可以将所有这些选项(
sep-dec-newline
)作为映射而不是键值对传递。可能的,但是我会有一个算术问题,我可以用defmulti解决。因此,这是一个解决方案,但现在选项在hashmap中,这对于老python/r用户来说不那么直观。但这仍然是一个非常好的主意,也许更多的是Clojure风格!谢谢你的回答,考虑到最初的需求,这个答案更准确。但最后你是对的,这有点难看。我使用我的第一个解决方案,即使OlegTheCat提出的地图选项看起来更“Clojureque”,只是因为它更符合我的库的精神。调试后,我的函数运行正常。
user> (defn parse
        {:arglists '([data header? path & {:keys [sep dec newline]
                                           :or {sep :aaa
                                                dec :bbb
                                                newline :ccc}}])}
        [data header-or-path & args]
        (let [[header path {:keys [sep dec newline]
                            :or {sep :aaa dec :bbb newline :ccc}}]
              (if (even? (count args))
                [nil header-or-path args]
                [header-or-path (first args) (rest args)])]
          (println data header path sep dec newline)))
#'user/parse

user> (parse 1 2)
1 nil 2 :aaa :bbb :ccc
nil

user> (parse 1 2 3)
1 2 3 :aaa :bbb :ccc
nil

user> (parse 1 2 :sep :a :dec :b)
1 nil 2 :a :b :ccc
nil

user> (parse 1 2 3 :sep :a :dec :b)
1 2 3 :a :b :ccc
nil
user/parse
 [data header? path & {:keys [sep dec newline], :or {sep :aaa, dec :bbb, newline :ccc}}]
  Not documented.
user/parse is defined in *cider-repl localhost*.