Clojure关键字和可选参数问题

时间:2010-11-26 18:59:14

标签: clojure argument-passing optional-parameters

我想创建一个接受必需参数x的函数,以及一个可选参数opt1或一个关键字参数opt2。

现在我有

(defn foo x & [opt1 {:keys [opt2]}]
  ...

但是上面的签名只允许我在x和opt1都存在时传递关键字参数opt2,如

(foo 'x 'opt1 {:opt2 'opt2})

不喜欢这个

(foo 'x {:opt2 'opt2})

请帮我创建一个带有必需参数X和opt1或opt2的函数,其中opt2是关键字参数。

谢谢。

编辑:我也希望对其他宏做同样的事情。所以我仍然需要使用defmacro。

2 个答案:

答案 0 :(得分:15)

问题是含糊不清。考虑一个函数(fn foo [x y & args]),它接受​​两个可选参数,然后是任意数量的关键字参数。如果你那么称它为(foo :bar :baz),你的程序如何处理它? x => :bary => :baz?或xy未提供,只有一个:bar => :baz关键字参数?

即使在Common Lisp中,在解析函数参数方面可能比Clojure更具灵活性,但至少one popular book不建议混合使用可选参数和关键字参数。

最好的办法是将所有参数更改为位置参数,或将所有参数更改为关键字参数。如果使用关键字参数,则可以使用哈希映射解构来为“可选”关键字参数提供默认值。

user> (defn foo [& {:keys [x y bar] 
                    :or {x 1 y 2 bar 3}}] 
        (prn [x y bar]))
#'user/foo
user> (foo)
[1 2 3]
nil
user> (foo :bar :baz)
[1 2 :baz]
nil

答案 1 :(得分:2)

你必须检查aditional参数是否是关键字参数(无论如何我假设你的或者是独家的),所以你可以这样做:

(defn foo [& args] 
    (if (= (count args) 1)
        (let [[opt1] args] (println opt1))
        (let [{:keys [opt2]} args] (println opt2))))

检查参数是否为关键字参数。由于您只有一个可选参数,因此很容易:检查是否只有一个作为关键字参数需要两个。