如何转发可选参数

问题描述 投票:2回答:1

通常情况下,我有一个函数,它接受一些可选参数并将它们传递给其他函数,这些函数将它们进一步传递到堆栈中,依此类推。如何才能在Clojure中完成,而不会出现我在下面说明的容易出错的复杂类型?

  1. 如果直接传递可选参数变量,则被调用者不能将其作为可选参数接受: (defn func1 [& {:keys [n-iterations] :or {n-iterations 20} :as opts}] (println "func1:" n-iterations) (func2 opts)) (defn func2 [& {:keys [n-iterations]}] (println "func2:" n-iterations)) user=> (func1 :n-iterations 15) func1: 15 IllegalArgumentException No value supplied for key: {:n-iterations 15} clojure.lang.PersistentHashMap.create (PersistentHashMap.java:77)
  2. 如果调用者需要可选参数作为非可选的map参数,那就是丑陋且容易出错,并且最重要的是,它会丢失默认值: (defn func2 [{:keys [n-iterations]}] ;lost the & (println "func2:" n-iterations)) user=> (func1 :n-iterations 15) func1: 15 func2: 15 nil user=> (func1) func1: 20 func2: nil nil 我听说你应该在顶级采用可选参数,在堆栈中所有较低级别采用非可选地图。我发现这不令人满意,因为通常,特别是在REPL,我想在任何“级别”调用任何函数,而不考虑是否有其他函数调用它。有一个统一的调用约定很有帮助。
  3. 如果转发一个不是由调用者提供的可选参数,Clojure会将其转换为nil,然后在堆栈的每一步中将其包装在ArraySeq中: (defn func1 [& opts] (println "func1:" opts) (func2 opts)) (defn func2 [& opts] (println (type opts)) (println "func2:" opts) (func3 opts)) (defn func3 [& opts] (println "func3:" opts)) user=> (func1) func1: nil func2: (nil) func3: ((nil))

大多数Clojure功能对我来说非常顺利,但事实并非如此。这样做的正确方法是什么?

以上都是在Clojure 1.9.0下。

clojure parameter-passing optional-parameters
1个回答
2
投票

如果直接传递可选参数变量,则被调用者不能将其作为可选参数接受

这不是严格意义上的,只是当两个函数都采用可变参数 - 在你的case关键字args中 - 并且你将它们解构为一个映射时,那么你必须将它们apply用于其他可变函数,就像你应用一个映射到他们:

(defn func2 [& {:keys [n-iterations]}]
  (println "func2:" n-iterations))
(defn func1 [& {:keys [n-iterations]
                :or {n-iterations 20}
                :as opts}]
  (println "func1:" n-iterations)
  (apply func2 (mapcat identity opts)))
(func1 :n-iterations 15) ;; works fine

你不能像func2那样直接调用(func2 {:n-iterations 20}),这实际上就是你的例子中发生的事情。

如果调用者需要可选参数作为非可选的map参数,那就是丑陋且容易出错,并且最重要的是,它会丢失默认值

在这种情况下,您仍然可以使用:or进行构造。

(defn func2 [{:keys [n-iterations]
              :or {n-iterations 10}}]
  (println "func2:" n-iterations))
(func2 nil) ;; prints "func2: 10"

如果转发一个不是由调用者提供的可选参数,Clojure会将其转换为nil,然后在堆栈的每一步中将其包装在ArraySeq中

我认为这只是对可变参数和解构如何工作的误解。在每个函数中,您接受可变参数并将它们绑定到单个名称opts。在你的函数体内,opts是一个集合。当您使用opts作为唯一参数调用其他可变参数函数时,您将它们作为一元函数调用。看看它是这样的:

(foo [1 2 3])       ;; this is the call style you're getting
(foo 1 2 3)         ;; this is the call style you want
(apply foo [1 2 3]) ;; how to call `foo` with a coll variadic-ly

这就是为什么apply需要你的可变参数,结构化到其他可变函数的集合参数。

还有另一种选择:

(defn foo [x y z & [{:keys [a b c}]] ...)

这是可变参数,但在第一个可选的arg位置采用可选的映射。

您还可以考虑使用here建议的函数的多个固定arm定义。

© www.soinside.com 2019 - 2024. All rights reserved.