Macros 如何编写模仿Ruby的Clojure宏';s%w运算符
我正试图用Clojure编写我的第一个宏。我想模拟Ruby的%w{}运算符,其工作原理如下:Macros 如何编写模仿Ruby的Clojure宏';s%w运算符,macros,clojure,Macros,Clojure,我正试图用Clojure编写我的第一个宏。我想模拟Ruby的%w{}运算符,其工作原理如下: irb(main):001:0> %w{one two three} => ["one", "two", "three"] user=> (%w one two three) CompilerException java.lang.RuntimeException: Unable to resolve symbol: one in this context, compiling:(N
irb(main):001:0> %w{one two three}
=> ["one", "two", "three"]
user=> (%w one two three)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: one in this context, compiling:(NO_SOURCE_PATH:1:1)
我想在Clojure中编写一个类似的函数,返回一个单词向量。下面是它的外观:
user=> (%w one two three)
=> ["one" "two" "three"]
我知道这不能定义为普通函数,因为符号在应用之前会被计算,我们会看到这样的结果:
irb(main):001:0> %w{one two three}
=> ["one", "two", "three"]
user=> (%w one two three)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: one in this context, compiling:(NO_SOURCE_PATH:1:1)
以下是我对宏的尝试:
(defmacro %w [& words]
(map str (vec words)))
但它不起作用
user=> (%w one two three)
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn user/eval801 (NO_SOURCE_FILE:1)
为什么会这样
答案
所以问题是宏实际上返回了正确的输出,但是repl试图对其求值,而“one”不是有效的函数
由于下面的答案,这里有两个解决此问题的正确宏:
(defmacro %w-vec [& words]
"returns a vector of word strings"
(mapv str (vec words)))
(defmacro %w-list [& words]
"returns a list of word strings"
(cons 'list (map str words)))
它不起作用,因为在宏展开之后,clojure尝试求值(“一”“二”“三”),从而导致错误消息
user=> (%w one two three)
ClassCastException java.lang.String ("one") cannot be cast to clojure.lang.IFn (interface for callable stuff) user/eval801 (NO_SOURCE_FILE:1)
现在你可以这么做了
(defmacro %w [& words]
(mapv str (vec words)))
生成向量
或者
(defmacro %w [& words]
(cons 'list (mapv str (vec words))))
正在生成(列表“一”“二”“三”)
或者使用语法引号
(defmacro %w [& words]
`(list ~@(map str (vec words))))
宏可以看作是常规函数,它接受未计算的代码(作为数据结构)并返回新代码(作为数据),然后对新代码进行计算 您的宏正在返回
(“一”“二”“三”)
,它将作为带有参数“二”“三”
的函数调用进行计算
简单的解决方案是让您的宏返回
(列出“一”“二”“三”)
或向量[“一”“二”“三”]
为什么没有人评论问题和所有答案中对向量的无用调用<代码>(mapf(vec x))
只是(mapfx)
,而(cons'list(mapv str(vec words)))
更糟糕:它应该是(cons'list(map str words))
。还请注意,在这里使用反引号而不是常规引号对列表
非常重要,否则,如果在列表
未引用clojure.core/list
的范围内,宏将失败,这可能是因为:在命名空间中引用clojure
,或者是因为本地名为的列表
。