Clojure 函数和线程宏

fcr*_*r79 1 clojure

我对 Clojure 语言还很陌生。

在阅读有关Clojure 函数的内容时,我找到了这个示例#([%])。所以我尝试按如下方式使用它:

(def test1 #([%]))
(test1 5)
Run Code Online (Sandbox Code Playgroud)

结果,我收到以下错误:

ArityException Wrong number of args (0) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)
Run Code Online (Sandbox Code Playgroud)

这似乎是它试图调用我想要返回的数组。

经过一番挖掘,我找到了解决方案,如下:

(def test1 #(-> [%]))
(test1 5)
Run Code Online (Sandbox Code Playgroud)

我有一些问题:

  1. 为什么工作不成#([%])?我对这个表情做了什么#([x])
  2. 在正确的示例中,我使用了线程优先宏。根据其文档,它用于将参数传递给下一个函数,例如(-> x (+ 1))。在这种情况下,我什至没有可以传递的函数;*在此上下文中的下一个功能是什么?我不明白为什么它解决了我的问题

Rul*_*lle 5

问题1

语法#([%])翻译为:“创建一个函数,在调用时将计算表达式([%]),并将%其作为传递给函数的第一个(也是唯一的)参数”。该表达式具有函数调用的语法,即[%]要调用的函数。您可以使用以下命令查看发生了什么macroexpand

(macroexpand '#([%]))
;; => (fn* [p1__6926#] ([p1__6926#]))
Run Code Online (Sandbox Code Playgroud)

clojure 中持久向量的类别是clojure.lang.PersistentVector。它们实现了IFnarity 1 的接口,以便您可以将向量视为将索引映射到元素的函数。但它们没有实现 arity 0,这正是您想要调用的。换句话说,你的代码不起作用:

(def test1 #([%]))
(test1 5) ;; ERROR
Run Code Online (Sandbox Code Playgroud)

但是,如果您将参数 0 传递给您的 function [%],您将返回该元素:

(def test1 #([%] 0))
(test1 5)
;; => 5
Run Code Online (Sandbox Code Playgroud)

你看到会发生什么吗?然而,对于您想要做的事情,有一个更好的方法:[a b c]语法只是调用的糖(vector a b c)。所以为了得到有用的东西,你可以这样做

(def test1 vector)
(test1 5)
;; => [5]
Run Code Online (Sandbox Code Playgroud)

问题2

线程优先宏的语法为(-> x f0 f1 f2 ...)wherex是初始值f0f1等等是函数调用,其第一个参数被省略,由正在通过管道传输的值替换。我们再次可以使用 Macroexpand 来理解:

(macroexpand '(-> x f0 f1 f2))
;; => (f2 (f1 (f0 x)))
Run Code Online (Sandbox Code Playgroud)

但在你的情况下,函数调用被忽略了。为了分析你的第二个例子,我们需要使用clojure.walk/macroexpand-all完整的扩展,因为我们有嵌套的宏:

(clojure.walk/macroexpand-all '#(-> [%]))
;; => (fn* [p1__6995#] [p1__6995#])
Run Code Online (Sandbox Code Playgroud)

不过,我们也可以一步一步地看:

(macroexpand '#(-> [%]))
;; => (fn* [p1__7000#] (-> [p1__7000#]))

(macroexpand '(-> [p1__7000#]))
;; => [p1__7000#]
Run Code Online (Sandbox Code Playgroud)

所以回答你的问题:中没有下一个函数(-> [%])。next 函数的数量可以是任何非负数,包括零, 就是这种情况(-> [%])