Clojure 如何转发可选参数
经常发生的情况是,我有一个函数,它接受一些可选参数并将它们传递给其他函数,这些函数将它们进一步传递给堆栈,以此类推。在Clojure中如何做到这一点,而不存在我在下面说明的容易出错的复杂性类型Clojure 如何转发可选参数,clojure,parameter-passing,optional-parameters,Clojure,Parameter Passing,Optional Parameters,经常发生的情况是,我有一个函数,它接受一些可选参数并将它们传递给其他函数,这些函数将它们进一步传递给堆栈,以此类推。在Clojure中如何做到这一点,而不存在我在下面说明的容易出错的复杂性类型 如果直接传递可选参数变量,则被调用方无法将其作为可选参数接受: (defn func1 [& {:keys [n-iterations] :or {n-iterations 20} :as opts}] (println "func1:" n-iterations) (func2 opts
(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)
(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中,我想在任何“级别”调用任何函数,而不管是否有其他函数调用它。有一个统一的电话会议会有很大帮助(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))
将它们应用到其他可变函数,就像您将映射应用到它们一样:
(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{:n-iterations 20})
那样直接调用func2
,这就是您的示例中实际发生的情况
如果调用者需要可选参数作为非可选映射参数,这很难看而且容易出错,而且会丢失默认值
在这种情况下,您仍然可以使用:或来分解结构
(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
这就是为什么有必要将
可变的、分解为集合的参数应用于其他可变函数的原因
还有一个选择:
(defn foo [x y z & [{:keys [a b c}]] ...)
这是可变的,但在第一个可选arg位置采用可选映射
<>你也可以考虑使用函数的多个固定的定义。