tsl*_*son 4 metaprogramming clojure
在我对Code Review.SE问题的回答中,我建议OP可以考虑使用记录来表示棋子.由于片段记录都是相同的,除了名称,我想我可以通过编程生成它们,如下所示:
(map #(defrecord % [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
Run Code Online (Sandbox Code Playgroud)
那种方式有用,但我的唱片名不是片名; 他们是随机的gensyms:而不是user.Rook我得到的user.p1__910.如果我这样做了(p1__910. :black),它确实有效并创造了一个记录,但你可能会明白为什么我对此并不满意.
我还尝试了以下两种变体:
(map #(defrecord % [color])
['Rook 'Pawn 'Queen 'King 'Knight 'Bishop])
;; Same result as above.
(map #(defrecord (symbol %) [color])
["Rook" "Knight" "Pawn" "Queen" "King" "Bishop"])
;; CompilerException java.lang.ClassCastException: clojure.lang.PersistentList
;; cannot be cast to clojure.lang.Symbol, compiling:(NO_SOURCE_PATH:1:7)
Run Code Online (Sandbox Code Playgroud)
我的做法有什么问题?如何从名单列表中生成一堆相同的记录?这甚至可能吗?
这是宏观传染的经典案例.
user> defrecord
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.core/defrecord, compiling:(/tmp/form-init802461651064926183.clj:1:5990)
Run Code Online (Sandbox Code Playgroud)
你(symbol %)提出这个想法非常接近你需要的想法,以便在你提供值之后评估生成的defrecord表达式.
user> (defmacro make-pieces [piece-names]
`(do ~@(map #(list 'defrecord (symbol %) '[color])
piece-names)))
#'user/make-pieces
user> (macroexpand-1 '(make-pieces ["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"]))
(do (defrecord Rook [color])
(defrecord Pawn [color])
(defrecord Queen [color])
(defrecord King [color])
(defrecord Knight [color])
(defrecord Bishop [color]))
user> (make-pieces ["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
user.Bishop
Run Code Online (Sandbox Code Playgroud)
如果所有记录都相同,为什么要给它们不同的名字?我建议:
(defrecord Chess-Piece [name color])
Run Code Online (Sandbox Code Playgroud)
你的方法有什么问题,那defrecord就是一个宏,所以"name"参数被解释为一个符号,因此在编译之前确定记录的名称.映射仅在运行时,编译后发生.
在%您的匿名函数被改写为gensym( p1__910),这反过来又被解释为符号命名你的新纪录.
您想要做的事情必须使用宏来完成 - 您必须简单地确保在(defrecord some-symbol [color])评估时间(再次,这是预运行时),some-symbol就是您想要的.也许有些东西:
(defmacro defpieces [names]
(let [defs (map #(list 'defrecord (symbol %) '[color])
names)]
`(do ~@defs)))
Run Code Online (Sandbox Code Playgroud)
您的代码如何被重写:
(map #(defrecord % [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
Run Code Online (Sandbox Code Playgroud)
使用阅读器宏,这变成(大致):
(map (fn* [p1__910#] (defrecord p1__910# [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
Run Code Online (Sandbox Code Playgroud)
defrecord 它本身就是一个宏,所以(再次,在运行之前)它被转换为一个巨大的代码块,其中包含:
(deftype* p1__910# user.p1__910# .....
Run Code Online (Sandbox Code Playgroud)
要查看整个块,请使用非常有用的宏扩展:
(macroexpand '(defrecord p1__910# [color]))
Run Code Online (Sandbox Code Playgroud)