Clojure关键字和可选参数问题

11
我想创建一个函数,该函数将接受一个必填参数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。

考虑使用来自clojure.contrib.defdefnk而不是显式解构。 - ffriend
从1.2版本开始,defnk已被弃用,建议采用更一致的内置功能。 - kotarak
2个回答

15
问题是歧义性。考虑一个函数(fn foo [x y & args]),它接受两个可选参数,然后是任意数量的关键字参数。如果你像这样调用它(foo :bar :baz),你的程序如何处理?x => :bary => :baz?还是没有提供xy,只有一个单一的:bar => :baz关键字参数?
即使在Common Lisp中,它在解析函数参数方面可能比Clojure的灵活性更大,但混合使用可选参数和关键字参数也不被推荐,至少一个流行的书籍是这样说的。
最好的方法是将所有参数都更改为位置参数,或将所有参数更改为关键字参数。如果使用关键字参数,则可以使用哈希映射解构来为“可选”关键字参数提供默认值。
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

2

您需要检查附加参数是关键字参数还是普通参数(我假设您的“or”是排他或),您可以按以下方式执行:

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

检查参数是否为关键字参数。因为你只有一个可选参数,所以很容易:检查是否只有一个参数,因为关键字参数需要两个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接