2015-04-20 2 views
4

В своем ответе на вопрос a Code Review.SE question я предложил, чтобы ОП мог использовать записи для представления фигур в шахматах. Так как штучные записи будут все то же самое, за исключением имени, я полагал, что я мог бы генерировать их программен, например:Как я могу программно генерировать определения записей?

(map #(defrecord % [color]) 
     ["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"]) 

Такого рода работал, но мои записи имена не штучные имен; они были случайными gensyms: вместо user.Rook Я получил . Если бы я сделал (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) 

Что случилось с моим подходом? Как я могу сгенерировать кучу идентичных записей из списка имен? Возможно ли это?

ответ

5

Это классический случай макроинтеграции.

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 [email protected](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 
+0

Моя неумелость с помощью макросов снова кусает меня. Спасибо за отличный ответ. – tsleyson

5

Если все записи одинаковы, зачем им давать им разные имена? Я хотел бы предложить:

(defrecord Chess-Piece [name color]) 

Что случилось с вашим подходом является то, что defrecord макрос, поэтому аргумент «имя» интерпретируется как символ и так определяет имя записи до компиляции. Отображение происходит только во время выполнения, после компиляции.

% в вашей анонимной функции переписан как gensym (p1__910), который, в свою очередь, интерпретируется как символ, обозначающий вашу новую запись.

То, что вы хотите сделать, должно быть выполнено с помощью макроса - вы должны просто убедиться, что к моменту оценки (defrecord some-symbol [color]) (опять же, это предварительное время выполнения), some-symbol - это то, что вы хотите.Может быть, что-то вдоль линий:

(defmacro defpieces [names] 
    (let [defs (map #(list 'defrecord (symbol %) '[color]) 
        names)] 
    `(do [email protected]))) 

Как ваш код переписан:

(map #(defrecord % [color]) 
    ["Rook" "Pawn" "Queen" "King" "Knight" "Bishop"]) 

С читателя макросов, это превращается в (примерно):

(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])) 
+1

Если у вас есть только одна запись в Chess-Piece, вы могли бы использовать парадигму Component Component для реализации ходов; каждая часть была бы сущностью, и каждый способ перемещения был бы компонентом. – galdre

+0

Отличное объяснение, спасибо. Мне было удобнее использовать отдельные записи, чтобы позволить отдельные реализации протокола для таких вещей, как ограничение количества мест, которые может перемещать король. Но я очень не знаю игрового программирования; если у вас есть другой подход, я готов поспорить с OP из вопроса [Code Review] (http://codereview.stackexchange.com/q/87325/52814), который вдохновил меня на то, чтобы спросить об этом, хотелось бы услышать об этом. – tsleyson

Смежные вопросы