我试图实施 xor 宏并想出了一个问题。
我无法在宏中使用私有功能。
以下是示例:
私人功能
(defn :^private xor-result
[x y]
(if (and x y)
false
(or x y)))
的宏
(defmacro xor
([] true)
([x] x)
([x & next]
`(let [first# ~x
second# ~(first next)]
(if (= (count '~next) 1)
(xor-result first# second#)
(xor (xor-result first# second#) ~@(rest next))))))
以下是错误:
CompilerException java.lang.IllegalStateException: var: #'kezban.core/xor-result is not public
删除^:私有标记时,解决问题。
问题是: 这种行为的原因是什么?
更新 :我可以将私人功能与以下方法一起使用。
私人功能
(defn ^:private xor-result
[x y]
(if (and x y)
false
(or x y)))
新宏
(defmacro xor
([] true)
([x] x)
([x & next]
(let [first x
second `(first '(~@next))
result (xor-result (eval first) (eval second))]
`(if (= (count '~next) 1)
~result
(xor ~result ~@(rest next))))))
答案 0 :(得分:5)
如果ns1
中有一个宏:
(ns ns1)
(defn- my-fun [x] (first x))
(defmacro my-macro [x] (my-fun ~x))
并在另一个名称空间中使用它:
(ns ns2
(:require [ns1 :refer [my-macro]]))
(my-macro [1 2])
编译器将在编译阶段调用宏,它将在 ns2
命名空间中生成代码,并将变为:
(ns ns2
(:require [ns1 :refer [my-macro]]))
(ns1/my-fun [1 2])
并且此代码最终将编译为字节代码。
正如您所看到的,编译器将在ns1
命名空间中看到ns2
私有函数的使用,并会抱怨它。
要调试宏,您可以使用macroexpand
查看应用宏的结果。
您还需要记住,您的宏可以处理您的程序数据:表示代码的数据结构(符号,列表,向量等)。例如,在宏的第二个版本中,它按原样运行符号,而不是绑定到它们的运行时值:
(macroexpand '(xor true false))
;; => (if (clojure.core/= (clojure.core/count (quote (false))) 1) true (boot.user/xor true))
(macroexpand '(xor (zero? 1) (zero? 0)))
;; => (if (clojure.core/= (clojure.core/count (quote ((zero? 0)))) 1) false (boot.user/xor false))
正如您所看到的,xor-result
函数不会使用实际运行时值调用,而是使用代表代码的数据调用。在编译期间直接在宏中调用xor-result
。在宏的第一个版本中,它在宏生成的代码中使用,在编译期间不会被调用。
答案 1 :(得分:0)
如果您确实想从公共宏中访问私有变量,则可以使用一种技巧,该私有宏将由其他命名空间使用。
当您通过在代码中引用var的值来解析它的值时,Clojure会检查var是公共的还是私有的,并且如果您尝试访问私有的var,编译器会抱怨。但是,您可以使用#'
语法显式地引用var本身(而不是其值),Clojure将允许这种引用甚至是私有var 。您应该使用标准名称(使用完整的名称空间名称),这样就不需要存在任何特定的名称空间别名。
因此,假设函数xor-result
位于名为mynamespace.core
的命名空间中,则您将调用以下函数:
(#'mynamespace.core/xor-result first# second#)