Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2008/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Macros 函数中的求值与Clojure中的宏_Macros_Clojure - Fatal编程技术网

Macros 函数中的求值与Clojure中的宏

Macros 函数中的求值与Clojure中的宏,macros,clojure,Macros,Clojure,考虑以下函数 (defn shove [data fun] (eval `(-> ~data ~fun))) 在这里,它可以像预期的那样工作 (shove [1 2 3] count) ;; ~~> 3 即使在这里,它预期也会失败,因为它计算(count)太早了 (shove [1 2 3] (count)) ;; ~~> clojure.lang.Compiler$CompilerException: clojure.lang.ArityException: ;;

考虑以下函数

(defn shove [data fun] (eval `(-> ~data ~fun)))
在这里,它可以像预期的那样工作

(shove [1 2 3] count)  ;; ~~> 3
即使在这里,它预期也会失败,因为它计算
(count)
太早了

(shove [1 2 3] (count))
;; ~~> clojure.lang.Compiler$CompilerException: clojure.lang.ArityException: 
;;       Wrong number of args (0) passed to: core$count, compiling:(null:5:1)
但在这里,当我定义一个显式表单并将其作为数据传递给函数时,一切都很好:

(def move '(count))
(shove [1 2 3] move)  ;; ~~> 3
现在,为了摆脱显式调用
eval
,我尝试

(defmacro shovem [data form] `(-> ~data ~form))
哪个很好

(shovem [1 2 3] count)    ;; ~~> 3
(shovem [1 2 3] (count))  ;; ~~> 3
但它现在在显式定义的表单
move
上意外失败,错误提示它计算
move
以获取
(count)
,然后继续尝试计算
(count)
,但方法与以前不同

(shovem [1 2 3] move)  
;; ~~> java.lang.ClassCastException: 
;; clojure.lang.PersistentList cannot be cast to clojure.lang.IFn
我对这个错误消息感到困惑,我不知道如何获得所需的行为,即
spoom
应该可以处理所有三种类型的输入、裸函数(如
count
)和括号函数(如
(count)
)以及数据对象(如
move
),这些数据对象的计算结果都是这样的

我可以在函数版本中使用
eval
,但此时,我意识到我不明白发生了什么,我想完成练习以提高我的理解。

解决方案? 在最一般的情况下,为了本练习的目的,您需要一个宏和
eval
,我假设这是为了学习(请不要实际这样做)

例如,保持
spoof
原样,并将其用作修改后的
spoom

(defn shove [x form] (eval `(-> ~x ~form)))

(defmacro shovem* [x form] 
  (if (seq? form) 
    (if (= 'quote (first form))
      `(-> ~x ~(second form))
      `(-> ~x ~form)) 
    `(shove ~x ~form)))
现在,
spowm*
具有您想要的语义

(def move '(count))

(shovem* [1 2 3] count) ;=> 3
(shovem* [1 2 3] (count)) ;=> 3
(shovem* [1 2 3] '(count)) ;=> 3
(shovem* [1 2 3] move) ;=> 3
(let [f count, d [1 2 3]] (shovem* d f)) ;=> 3

原始宏的问题(?) 这就结束了宏扩展阶段。现在
(移动[1 2 3])
就是代码。评估时会发生什么

user=> (move [1 2 3])
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IFn 
如果原因不明显,你需要重新考虑。表单
(move[1 2 3])
是一个列表,而
move
不是特殊的表单或宏。因此,这被认为是对其参数
[1 2 3]
move
函数调用。但是什么是移动

user=> (type move)
clojure.lang.PersistentList

user=> (ifn? move)
false
所以,
move
不是一个函数,也不知道如何像函数一样工作。这只是一张单子

user=> (= move (list 'count))
true    

在代码中,
spowm
传递符号,而不是move的实际值,因为它是一个宏。因此,对
spowm
的调用将扩展到:

(-> [1 2 3] move)
->
是另一个隐式将
移动
包装在列表中的宏,因为它是一个符号,所以此代码相当于:

(-> [1 2 3] (move))
这就是为什么在
->
完全展开后,它会变为

(move [1 2 3])
move
是一个序列,而不是一个函数,因此
java.lang.ClassCastException


我不确定让宏对所有输入都工作的目标是否可行,因为作为一个在任何代码执行之前运行的宏,它不知道传递的
move
符号是应该评估的(以获取(计数))还是只是字面上使用。一般来说,宏知道是否应该对每个参数求值或求值次数,只基于传递给它的参数的形式,而不是它们的运行时值。

尝试
macroexpand
查看宏调用实际生成的内容
(move [1 2 3])