gpt4 book ai didi

Clojure : more than 1 variadic overload, 更惯用的做法?

转载 作者:行者123 更新时间:2023-12-02 16:49:48 25 4
gpt4 key购买 nike

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

(defn write-csv
"Writes a csv from data that is already formatted for clojure.data.csv/write-csv or not.
In the second case, the function guesses a header and writes it. Can handle three types of
data : nested rows (example : {1 {:a 2 :b 3} 2 {:a 25 :b 17} ...), flattened data (like the one you use
in clj-data-process-utils.data (example : ({:id 1 :a 2 :b 3} {:id 2 :a 25 :b 17} ...)) or already formatted
data (example : [['ID' 'B' 'C'] [1 2 3] [2 25 17]]). Note that in the last case you have to provide a header if you want one.
The guesses can be overriden by the :header arg. Optimized for Excel with default values.
Input :
- data : data to write as CSV
- path : the filepath of the new CSV
- (optional) sep : the separator to use, must be of type char [default : ;]
- (optional) dec : the decimal separator to use, must be of type char [default : .]
- (optional) newline : the newline character, see cljure.data.csv options, default here for windows [default : :cr+lf]
- (optional) header : if you want to provide your own data, pass here a vector of columns names, guesses by default if data is not formatted [default : :guess]"
[data path & {:keys [sep dec newline header] :or {sep \; dec \. newline :cr+lf header :guess}}]
(let [f-data (cond (or (map? data) (seq? data))
(cond (vec? header)
(format-for-csv sep data header)
(= :guess header)
(->> (guess-header data)
(format-for-csv sep data)))
(vec? data)
data)
wrtr (io/writer path)]
(csv/write-csv wrtr f-data :separator sep :newline newline)))

如您所见,我们可以选择传递 header 。我把它放在可选键上,但我更愿意在第一个实例中拥有这样的东西(即使这个 aritties map 对我来说没问题):

(defn write-csv 
([data path & {:keys [sep dec newline] :or {sep \; dec \. newline :cr+lf}}]
...)
([data header path & {:keys [sep dec newline] :or {sep \; dec \. newline :cr+lf}}]
...))

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

我对两件事非常坚持:

  • 使用第二个私有(private)函数与 apply...但它不能解决第一个问题,因为我想要两种可能的输入样式。
  • 我查看了 defmulti,但我发现每个子方法也采用相同的参数

当然,我也可以将函数分成两个或区分第一个参数中的两种情况(类型为[向量图]的向量意味着用户传递了未格式化的数据+ header ),但这对于用户。我真的很想提供这些输入的可能性。

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

谢谢!

最佳答案

我想,将所有关键字参数保留在单独的映射中是最好的解决方案。但是,如果您确实需要(我认为不是),还有一种相对流行的方法可以做到这一点:您可以使用一种可变参数签名与arglists元数据相结合:

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

但同样:这太冗长且难以维护(由于代码重复),因此您应该明智地使用它(我的意思是根本没有人应该这样做)

关于Clojure : more than 1 variadic overload, 更惯用的做法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37614331/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com