成衣规则如何绑定变量参数?



我是Clojure的新手,问题源于我曾经检查过conj的源代码:

(def conj 
(fn ^:static conj
([] [])
([coll] coll)
([coll x] (clojure.lang.RT/conj coll x));4
([coll x & xs] ;1
(if xs ;2
(recur (clojure.lang.RT/conj coll x) (first xs) (next xs)) ;3
(clojure.lang.RT/conj coll x)))))

conj的源代码表明它使用 recur 来实现函数。这个源代码看起来非常简单。 我感到困惑的是它在确定递归是否需要继续时使用的条件。看起来它检查变量参数是否nil,但如果变量参数nil,它很快就会等价于conj的第三个"arity"? 然后,我尝试评估以下表达式:

user=> (conj [] 1 (next []))
[1 nil]
user=>

它正常工作并成功地将nil添加到向量中。我知道 clojure 实际上将nil包装在一个列表中并将其传递给函数,但我不明白为什么recur可以传递一个真正的nil?为什么 clojure 会识别并匹配正确的"arity"?


user=> (def my_conj
(fn [coll x & xs]
(println "xs is" xs)
(if xs
(recur (clojure.lang.RT/conj coll x) (first xs) (next xs))
(clojure.lang.RT/conj coll x))))
#'user/my_conj
user=> (my_conj [] 1 (next []))
xs is (nil)
xs is nil
[1 nil]

好的,我很抱歉没有意识到你之前遇到了我以前从未见过的Clojure行为的一个方面。 今天,我学到了一些关于Clojure的新知识,并用了10年的时间,这让我感到惊讶。

这在官方 Clojure 文档的几句话中提到过:https://clojure.org/reference/special_forms#recur

这里有一页社区编写的示例和文档(所以不是"官方的"(,描述了这种使用具有可变参数的函数的递归行为:https://clojuredocs.org/clojure.core/recur#example-55ff3cd4e4b08e404b6c1c7f

我建议复制这个函数,将其名称更改为其他名称,并添加一些调用来println您感兴趣的任何值,例如 x、xs 等,并观察当您使用您感兴趣的不同参数调用函数时在 Clojure REPL 会话中打印的内容。

如果xs的值是除nilfalse以外的任何值,则if xs为 true。 值(nil)是一个元素的列表,当用作if条件表达式时,该值被视为 true。

函数中用参数[coll x & xs]声明的部分意味着"将第一个参数的值绑定到 coll,将第二个参数的值绑定到 x,然后如果没有更多参数,则将 xs 与值 nil 绑定,否则将 xs 与作为剩余参数列表的值绑定"。

你可以通过这个根本不使用recur的简单函数看到这一点:

user=> (defn my-fn [a & xs]
(println "a=" a " xs=" xs))
user=> (my-fn 1)
a= 1  xs= nil
user=> (my-fn 1 2)
a= 1  xs= (2)

相关内容

  • 没有找到相关文章

最新更新