Clojure-命名参数


78

Clojure是否有命名参数?如果是这样,您能否提供一个小例子?

Answers:


118

在Clojure 1.2中,可以rest像解构地图一样对自变量进行解构。这意味着您可以执行命名的非位置关键字参数。这是一个例子:

user> (defn blah [& {:keys [key1 key2 key3]}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there" :key3 10)
"Hai there10"
user> (blah :key1 "Hai" :key2 " there")
"Hai there"
user> (defn blah [& {:keys [key1 key2 key3] :as everything}] everything)
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
{:key2 " there", :key1 "Hai"}

分解Clojure映射时可以执行的任何操作都可以在函数的参数列表中完成,如上所示。包括使用:or来定义类似这样的参数的默认值:

user> (defn blah [& {:keys [key1 key2 key3] :or {key3 10}}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

但这是在Clojure 1.2中。或者,在较旧的版本中,您可以执行以下操作来模拟同一件事:

user> (defn blah [& rest] (let [{:keys [key1 key2 key3] :or {key3 10}} (apply hash-map rest)] (str key1 key2 key3)))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

而且大致相同。

您还可以在关键字参数之前添加位置参数:

user> (defn blah [x y & {:keys [key1 key2 key3] :or {key3 10}}] (str x y key1 key2 key3))
#'user/blah
user> (blah "x" "Y" :key1 "Hai" :key2 " there")
"xYHai there10"

这些不是可选的,必须提供。

实际上,您可以rest像处理任何Clojure集合一样破坏参数的结构。

user> (defn blah [& [one two & more]] (str one two "and the rest: " more))
#'user/blah
user> (blah 1 2 "ressssssst")
"12and the rest: (\"ressssssst\")"

即使在Clojure 1.1中,您也可以做这种事情。地图样式的关键字参数解构仅在1.2中出现。


34

除了Raynes的出色答案,clojure-contrib中还有一个宏,可以使生活更轻松:

user =>(使用'[clojure.contrib.def:only [defnk]])
零
用户=>(defnk foo [ab:c 8:d 9] 
         [A B C D])
#'user / foo
用户=>(foo 1 2)
[1 2 8 9]
用户=>(foo 1 2 3)
java.lang.IllegalArgumentException:没有为键提供值:3(NO_SOURCE_FILE:0)
用户=>(foo 1 2:c 3)
[1 2 3 9]

6
我忘了提!我全神贯注于展示Clojure分解事物的一万种方法。:p
Rayne

1
clojure-contrib已过时,我找不到当前的替代方法。有任何想法吗?
Lstor

2
Ian

3

从Clojure 1.8版开始,关键字支持似乎仍然有点麻烦

您可以像这样指定关键字参数:

(defn myfn1
  "Specifying keyword arguments without default values"
  [& {:keys [arg1 arg2]}]
  (list arg1 arg2))

调用示例:

(myfn1 :arg1 23 :arg2 45)  --> evaluates to (23 45)
(myfn1 :arg1 22)           --> evaluates to (22 nil)

如果要为这些关键字参数指定默认值:

(defn myfn2
  "Another version, this time with default values specified"
  [& {:keys [arg1 arg2] :or {arg1 45 arg2 55}}]
  (list arg1 arg2))

在第二种情况下,这可以完成预期的操作:

(myfn2 :arg1 22)           --> evaluates to (22 55)

每种语言的每个部分都有优点和缺点,但是仅作比较,这就是您在Common Lisp中做相同事情的方式:

(defun myfn3
    (&key arg1 arg2)
    "Look Ma, keyword args!"
    (list arg1 arg2))

(defun myfn4
    (&key (arg1 45) (arg2 55))
    "Once again, with default values"
    (list arg1 arg2))

0

您可能是指命名参数吗?这些不是直接可用的,但是您可以根据需要使用此向量方法,这可能会为您提供所需的内容。

在RosettaCode中,对如何使用解构进行了更深入的说明。


3
@Abel您可以分享链接到的示例吗?(他们有一种改变或过时的方法)。
David J.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.