如何以编程方式生成记录定义?
How can I programmatically generate record definitions?
在我对a Code Review.SE question的回答中,我建议OP可以考虑使用记录来表示棋子。由于片段记录都是相同的,除了名称,我想我可以通过编程方式生成它们,如下所示:
(map #(defrecord % [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
这有点奏效,但我的唱片名称不是曲目名称;它们是随机的 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)
我的方法有什么问题?如何从名称列表中生成一堆相同的记录?这可能吗?
这是宏观传染的经典案例。
user> defrecord
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.core/defrecord, compiling:(/tmp/form-init802461651064926183.clj:1:5990)
您与 (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
如果所有记录都相同,为什么要给它们不同的名称?我建议:
(defrecord Chess-Piece [name color])
你的方法有什么问题是 defrecord
是一个宏,所以 "name" 参数被解释为一个符号,因此在编译之前 确定了记录的名称。映射仅发生在运行时,编译后。
匿名函数中的 %
被重写为 gensym (p1__910
),它又被解释为命名新记录的符号。
你想做的事情必须用宏来完成——你必须简单地确保在评估 (defrecord some-symbol [color])
时(同样,这是运行前),some-symbol
是你想要的。也许是这样的:
(defmacro defpieces [names]
(let [defs (map #(list 'defrecord (symbol %) '[color])
names)]
`(do ~@defs)))
您的代码是如何重写的:
(map #(defrecord % [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
使用 reader 宏,这变成(大致):
(map (fn* [p1__910#] (defrecord p1__910# [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
defrecord
本身就是一个宏,因此(同样,在运行前)它被转换成一个巨大的代码块,其中包含:
(deftype* p1__910# user.p1__910# .....
要查看整个块,请使用非常有用的 macroexpand:
(macroexpand '(defrecord p1__910# [color]))
在我对a Code Review.SE question的回答中,我建议OP可以考虑使用记录来表示棋子。由于片段记录都是相同的,除了名称,我想我可以通过编程方式生成它们,如下所示:
(map #(defrecord % [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
这有点奏效,但我的唱片名称不是曲目名称;它们是随机的 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)
我的方法有什么问题?如何从名称列表中生成一堆相同的记录?这可能吗?
这是宏观传染的经典案例。
user> defrecord
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.core/defrecord, compiling:(/tmp/form-init802461651064926183.clj:1:5990)
您与 (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
如果所有记录都相同,为什么要给它们不同的名称?我建议:
(defrecord Chess-Piece [name color])
你的方法有什么问题是 defrecord
是一个宏,所以 "name" 参数被解释为一个符号,因此在编译之前 确定了记录的名称。映射仅发生在运行时,编译后。
匿名函数中的 %
被重写为 gensym (p1__910
),它又被解释为命名新记录的符号。
你想做的事情必须用宏来完成——你必须简单地确保在评估 (defrecord some-symbol [color])
时(同样,这是运行前),some-symbol
是你想要的。也许是这样的:
(defmacro defpieces [names]
(let [defs (map #(list 'defrecord (symbol %) '[color])
names)]
`(do ~@defs)))
您的代码是如何重写的:
(map #(defrecord % [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
使用 reader 宏,这变成(大致):
(map (fn* [p1__910#] (defrecord p1__910# [color])
["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"])
defrecord
本身就是一个宏,因此(同样,在运行前)它被转换成一个巨大的代码块,其中包含:
(deftype* p1__910# user.p1__910# .....
要查看整个块,请使用非常有用的 macroexpand:
(macroexpand '(defrecord p1__910# [color]))