0

任意のCLOSオブジェクトを取得し、そこからハッシュを返すメカニズムを構築しています(デバッグの経験で役立ちます)。

ただし、変数の展開を強制する方法がわかりません。解決策はgensymを正しく使用することにあると思いますが、その方法はわかりません。

;;helper macro
(defun class-slots-symbols (class-name)
  "Returns a list of the symbols used in the class slots"
  (mapcar 'closer-mop:slot-definition-name
      (closer-mop:class-slots
       (find-class class-name))))

;;macro that I am having difficulty with
(defmacro obj-to-hash (obj-inst)
  "Reads an object, reflects over its slots, and returns a hash table of them"
  `(let ((new-hash (make-hash-table))
    (slot-list (class-slots-symbols (type-of ,obj-inst))))

    ;;The slot-list needs to expand out correctly in the with-slots form
    (with-slots (slot-list) obj-inst
       (loop for slot in slot-list do   ;and also here
        (format t "~a~&" slot)
        (hashset new-hash (string slot) slot)))))

*bar*macroexpand-1の後、これが次のコード(クラスオブジェクト)に展開されることがわかりました。

(macroexpand-1 '(obj-to-hash *bar*))

LET ((NEW-HASH (MAKE-HASH-TABLE))
      (SLOT-LIST (CLASS-SLOTS-SYMBOLS (TYPE-OF *BAR*))))
  (WITH-SLOTS (SLOT-LIST)  ;; <-- this needs to be expanded to *bar*'s slots
      *BAR*
    (LOOP FOR SLOT IN SLOT-LIST ;;<-- not so important
          DO (FORMAT T "~a~&" SLOT) (HASHSET NEW-HASH (STRING SLOT) SLOT))))

明らかに、問題はスロットリストが拡張されていないことです。(私には)あまり明白ではないのが解決策です。


フォローアップ:レイナーが私を正しい方向に向けた後:

(defun class-slots-symbols (class-instance)
  "Returns a list of the symbols used in the class slots"
  (mapcar 'closer-mop:slot-definition-name
      (closer-mop:class-slots
       (class-of class-instance))))

(defun object-to-hash (obj)
  "Reflects over the slots of `obj`, and returns a hash table mapping
slots to their values"
  (let ((new-hash (make-hash-table))
    (slot-list (class-slots-symbols obj)))
    (loop for slot in slot-list do
     (hashset new-hash (string slot) 
          (slot-value  obj slot)))
    new-hash))
4

1 に答える 1

7

これを見ただけでは、これがマクロであるべき理由がわかりません。関数として書き直すと、多くの手間が省けます。

WITH-SLOTS の使用は、試してみても不可能です。通常、オブジェクトは実行時までわかりません。コンパイラは、コンパイル時に既にオブジェクトのスロットを認識している必要があります。SLOT-VALUE を使用して、実行時にスロット値を検索する必要があります。

多くの点で複雑すぎて、コードが少し混乱しています。簡単なルールに従い、言葉遣いを避けることで、混乱を取り除くことができます。

あなたのコードを見てみましょう

まず、これはヘルパー マクロではありません。これは、後に続くものが関数であるためです。

;;helper macro
(defun class-slots-symbols (class-name)

なぜクラス名を取るのですか?クラス自体を使用しないのはなぜですか?クラスはファーストクラスのオブジェクトです。明確なインターフェイスを使用して関数を記述します。初等関数は、基本的なデータ型で機能する必要があります。

  "Returns a list of the symbols used in the class slots"

クラス スロットでは、シンボルは使用されません。スロットには名前があり、このシンボルを取得できます。

  (mapcar 'closer-mop:slot-definition-name
      (closer-mop:class-slots
       (find-class class-name))))

このマクロに問題があるのも不思議ではありません。それはマクロではなく関数であるべきだからです。マクロはソース変換用です。必要なのは簡単な計算だけなので、マクロは必要ありません

;;macro that I am having difficulty with
(defmacro obj-to-hash (obj-inst)

不適切な表現: obj-inst. オブジェクトまたはインスタンスのいずれかの名前を付けます。両方ではありません。

  "Reads an object, reflects over its slots, and returns a hash table of them"

貧弱なドキュメンテーション: 何も読んでいません。読み取りは I/O 操作であり、コードには何もありません。「オブジェクト」について話しているのですが、上には「obj-inst」のようなものがあります。同じことを2つの異なる方法で話すのはなぜですか? ハッシュ テーブルが実際にマップするものを文書化したい場合があります。どのキーからどの値へ?

  `(let ((new-hash (make-hash-table))

new-hash も不適切な名前です。基本的にはハッシュテーブルです。

    (slot-list (class-slots-symbols (type-of ,obj-inst))))

TYPE-OF の後にヘルパー関数で FIND-CLASS を呼び出すのはなぜですか? Common Lisp には、クラスを直接返す CLASS-OF があります。

    ;;The slot-list needs to expand out correctly in the with-slots form
    (with-slots (slot-list) obj-inst

WITH-SLOTS はスロットリストではなく、コンパイル時にスロット名を想定しているため、上記は機能しません。

       (loop for slot in slot-list do   ;and also here
        (format t "~a~&" slot)
        (hashset new-hash (string slot) slot)

特別なことをしない限り、HASHSET は必要ありません。値を設定する通常の方法は、SETF を使用することです。SETF は、場所を読み取るためのフォームと、値を計算するためのフォームを取ります。それで全部です。あらゆる種類のデータ構造で機能します。ライター関数がどのように見えるか (名前、パラメーター リストなど) を再度覚える必要はありません。

))))

ここに私のバージョンがあります:

パッケージCLOSを使用していることに注意してください。パッケージCLOSER-MOPを使用することをお勧めします

(defun class-slots-symbols (class)
  "Returns a list of the symbol names of the class slots"
  (mapcar 'clos:slot-definition-name
          (clos:class-slots class)))

上記は、クラスを受け取り、スロット名のリストを返す単純な関数です。

次に、Common Lisp で 100 万回もこの形で書かれた単純な関数があります。

(defun object-to-hash (object)
  "returns a hashtable with the object's slots as keys and slot-values as values"
  (let ((hash-table (make-hash-table)))
    (loop for slot-name in (class-slots-symbols (class-of object))
          do (setf (gethash slot-name hash-table)
                   (string (slot-value object slot-name))))
    hash-table))

少し古いスタイルの Lisp に書き直すこともできます。

(defun object-to-hash (object &aux (hash-table (make-hash-table)))
  "returns a hashtable with the object's slots as keys
   and string versions of the slot-values as values"
  (dolist (slot-name (class-slots-symbols (class-of object)) hash-table)
    (setf (gethash slot-name hash-table)
          (string (slot-value object slot-name)))))

上記ははるかに単純で、マクロ、コードの生成、コンパイル時の情報とランタイムなどに関する混乱がすべて削除されています。理解、保守、およびデバッグがはるかに簡単です。

于 2011-06-25T20:14:28.707 に答える