Macros 计算Clojure标记文字的参数
我一直在尝试使用Clojure标记的文本,并注意到读者不像宏那样计算参数。这是有道理的,但这样做的合适解决方案是什么?显式评估 示例:给定此函数Macros 计算Clojure标记文字的参数,macros,clojure,literals,Macros,Clojure,Literals,我一直在尝试使用Clojure标记的文本,并注意到读者不像宏那样计算参数。这是有道理的,但这样做的合适解决方案是什么?显式评估 示例:给定此函数 (defn my-data ([[arg]] (prn (symbol? arg)) :ok)) 这个定义data\u readers.clj {myns/my-data my.ns/my-data} 以下行为不同: > (let [x 1] (my.ns/my-data [x])) false :ok 因此,传入的
(defn my-data
([[arg]]
(prn (symbol? arg))
:ok))
这个定义data\u readers.clj
{myns/my-data my.ns/my-data}
以下行为不同:
> (let [x 1] (my.ns/my-data [x]))
false
:ok
因此,传入的x
在传入my data
之前进行评估。另一方面:
> (let [x 1] #myns/my-data [x])
true
:ok
因此,如果我想在my data
中使用x
的值,则my data
函数需要对其进行处理,即检查x
是否为符号,如果是,则使用(eval x)
。那看起来很难看。有更好的方法吗?总结
在您的示例中,无法获取本地x
的值,这主要是因为本地值仅在运行时获得赋值,而标记的文本则在读取时处理。(其间还有编译时间;不可能在编译时获取局部值;因此宏也无法获取局部值。)1
更好的方法是在运行时使用常规函数,因为毕竟您希望基于某些参数的运行时值构造一个值。标记的文字实际上是文字,应该这样使用
扩大讨论
为了说明上述问题:
(binding [*data-readers* {'bar (fn [_] (java.util.Date.))}]
(eval (read-string "(defn foo [] #bar x)")))
foo
将始终返回相同的值,因为读取器只有一次机会返回#bar x
的值,然后该值被烘焙到foo
的字节码中
还要注意的是,我们可以存储调用readstring
返回的数据结构,而不是直接将其传递给eval
,并在将来的任意时刻编译它;foo
返回的值将保持不变。很明显,这样一个文字价值不可能依赖于任何当地人的未来价值;事实上,在读卡器的操作过程中,甚至不清楚哪些符号将被命名为局部变量——这是由编译器决定的,在涉及宏的情况下,决定的结果可能不明显
当然,读者可以自由返回一个类似于函数调用、本地名称等的表单,以显示一个示例:
(binding [*data-readers* {'bar (fn [sym] (list sym 1 2 3 4 5))}]
(eval (read-string "#bar *")))
;= 120
;; substituting + for * in the string yields a value of 15
这里的#条f
相当于(f 1 2 3 4 5)
。不用说,这是对符号的滥用,并没有真正做到你所要求的
1值得指出的是,
eval
无法访问局部变量(它总是在全局范围内运行),但局部变量在运行前没有赋值的问题更为根本。Summary
在您的示例中,无法获取本地x
的值,这主要是因为本地值仅在运行时获得赋值,而标记的文本则在读取时处理。(其间还有编译时间;不可能在编译时获取局部值;因此宏也无法获取局部值。)1
更好的方法是在运行时使用常规函数,因为毕竟您希望基于某些参数的运行时值构造一个值。标记的文字实际上是文字,应该这样使用
扩大讨论
为了说明上述问题:
(binding [*data-readers* {'bar (fn [_] (java.util.Date.))}]
(eval (read-string "(defn foo [] #bar x)")))
foo
将始终返回相同的值,因为读取器只有一次机会返回#bar x
的值,然后该值被烘焙到foo
的字节码中
还要注意的是,我们可以存储调用readstring
返回的数据结构,而不是直接将其传递给eval
,并在将来的任意时刻编译它;foo
返回的值将保持不变。很明显,这样一个文字价值不可能依赖于任何当地人的未来价值;事实上,在读卡器的操作过程中,甚至不清楚哪些符号将被命名为局部变量——这是由编译器决定的,在涉及宏的情况下,决定的结果可能不明显
当然,读者可以自由返回一个类似于函数调用、本地名称等的表单,以显示一个示例:
(binding [*data-readers* {'bar (fn [sym] (list sym 1 2 3 4 5))}]
(eval (read-string "#bar *")))
;= 120
;; substituting + for * in the string yields a value of 15
这里的#条f
相当于(f 1 2 3 4 5)
。不用说,这是对符号的滥用,并没有真正做到你所要求的
1值得指出的是,
eval
无法访问局部变量(它总是在全局范围内运行),但局部变量在运行前没有赋值的问题更为根本。Summary
在您的示例中,无法获取本地x
的值,这主要是因为本地值仅在运行时获得赋值,而标记的文本则在读取时处理。(其间还有编译时间;不可能在编译时获取局部值;因此宏也无法获取局部值。)1
更好的方法是在运行时使用常规函数,因为毕竟您希望基于某些参数的运行时值构造一个值。标记的文字实际上是文字,应该这样使用
扩大讨论
为了说明上述问题:
(binding [*data-readers* {'bar (fn [_] (java.util.Date.))}]
(eval (read-string "(defn foo [] #bar x)")))
foo
将始终返回相同的值,因为读取器只有一次机会返回#bar x
的值,然后该值被烘焙到foo
的字节码中
还要注意的是,我们可以存储调用readstring
返回的数据结构,而不是直接将其传递给eval
,并在将来的任意时刻编译它;foo
返回的值将保持不变。很明显,这样一个文字值不可能依赖于未来