功能打印在clojure中有bug吗?

时间:2015-12-24 07:11:06

标签: clojure macros eval

我写了一个宏:

(defmacro te
  [a b & c]
  `(print
     ~(a b c)))

并运行

(te print 2 inc 4)

收到错误ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval8010 (form-init8519408738377285198.clj:1)

我跑

(macroexpand-1 '(te print 2 3 4)
;=> (clojure.core/print (3 4))

这意味着(print 2 (3 4))返回(3 4)?函数print有错误吗?

我的clojure版本1.7.0,JVM版本1.8.0_65-b17

更新

好的。举例不容易理解这个例子。

即使我运行

(te print 2 inc 4)

user=> (te print 2 inc 4)
;=> 5nil

user=> (macroexpand-1 '(te print 2 inc 4))
;=> (clojure.core/print (inc 4))

它将打印5并返回nil,这意味着(print 2 (inc 4))返回表单(inc 4)

2 个答案:

答案 0 :(得分:8)

这与print根本无关。

  

这意味着(print 2 (3 4))返回(3 4)

这不是它的意思,而且这是你问题的根源。这意味着"用第一个参数print调用函数2,用(3 4)"的值调用第二个参数。表达式(3 4)没有有效值,因为它表示"使用参数3调用函数4"这就是为什么你得到一个例外:3是一个Long(数字)并且不能被称为函数(在clojure内部,它没有实现IFn函数接口)。

作为旁注,如果我理解你想要实现的目标(我可能是错的),你的宏可以很容易地写成一个函数,这通常意味着你应该把它写成一个函数,因为函数更容易处理,并与其他功能更好地合作。

答案 1 :(得分:0)

请记住宏观时尚未评估宏天堂的参数 被调用。

当您像这样调用宏时:

  

(te print 2 inc 4)

参数绑定到以下内容:

  • a:'print
  • b:'2
  • c:'(inc 4)(因为宏是可变参数,所有参数都超过前两个参数 进入c
  • 中的列表

你最终得到的表达是:

  

`(打印〜('打印' 2'(包括4)))

问题在于:内部print 与功能相同 clojure.core/print。这是一个宏观,因此它是一个未评估的符号。你正在使用 在这种情况下,符号本身就是一个函数,而不是使用引用的函数 通过符号。

Clojure中的每个符号都是一个在地图中查找自己的函数。 查找需要两个 参数,键和默认值。在这种情况下,符号print正试图查看 由于2不是地图,因此它返回默认值(inc 4)

回想一下,unquote(~)意味着评估表达式并替换结果 进入宏生成的代码。

所以编译器评估:('print '2 '(inc 4))然后返回(inc 4) 将其替换为原始表达。

这就是你最终得到(clojure.core/print (inc 4))

的方式

试试这个:

  

(macroexpand-1'(te blarg 2 inc 4))

你甚至得到完全相同的结果 虽然blarg不是Clojure中任何地方定义的变量。

在你的另一个例子中,另一个答案是对的。如果你的宏的第三个参数不是函数,那么你会在那里得到另一个错误。

老实说,我不确定你要完成什么,所以我不确定这是否真的能回答你的问题。但这就是为什么你会看到你所看到的东西。