9

次のコードをコンパイルすると、SBCL は g!-unit-value と g!-unit が定義されていないと文句を言います。これをデバッグする方法がわかりません。私が知る限り、フラット化は失敗しています。

flatten が defunit の引用符で囲まれていない部分に到達すると、その部分全体がアトムとして扱われているように見えます。その音は正しいですか?

以下は、本Let over Lambdaのコードを使用しています。

ポール・グラハム・ユーティリティ

(defun symb (&rest args)
  (values (intern (apply #'mkstr args))))

(defun mkstr (&rest args)
  (with-output-to-string (s)
    (dolist (a args) (princ a s))))

(defun group (source n)
  (if (zerop n) (error "zero length"))
  (labels ((rec (source acc)
             (let ((rest (nthcdr n source)))
               (if (consp rest)
                   (rec rest (cons (subseq source 0 n) acc))
                   (nreverse (cons source acc))))))
    (if source (rec source nil) nil)))

(defun flatten (x)
  (labels ((rec (x acc)
             (cond ((null x) acc)
                   ((atom x) (cons x acc))
                   (t (rec (car x) (rec (cdr x) acc))))))
    (rec x nil)))

Let Over Lambda ユーティリティ - 第 3 章

(defmacro defmacro/g! (name args &rest body)
  (let ((g!-symbols (remove-duplicates
               (remove-if-not #'g!-symbol-p
                              (flatten body)))))
    `(defmacro ,name ,args
       (let ,(mapcar
              (lambda (g!-symbol)
                `(,g!-symbol (gensym ,(subseq
                                       (symbol-name g!-symbol)
                                       2))))
              g!-symbols)
         ,@body))))

(defun g!-symbol-p (symbol-to-test)
  (and (symbolp symbol-to-test)
       (> (length (symbol-name symbol-to-test)) 2)
       (string= (symbol-name symbol-to-test)
                "G!"
                :start1 0
                :end1 2)))

(defmacro defmacro! (name args &rest body)
  (let* ((o!-symbols (remove-if-not #'o!-symbol-p args))
         (g!-symbols (mapcar #'o!-symbol-to-g!-symbol o!-symbols)))
    `(defmacro/g! ,name ,args
       `(let ,(mapcar #'list (list ,@g!-symbols) (list ,@o!-symbols))
          ,(progn ,@body)))))

(defun o!-symbol-p (symbol-to-test)
  (and (symbolp symbol-to-test)
       (> (length (symbol-name symbol-to-test)) 2)
       (string= (symbol-name symbol-to-test)
                "O!"
                :start1 0
                :end1 2)))

(defun o!-symbol-to-g!-symbol (o!-symbol)
  (symb "G!" (subseq (symbol-name o!-symbol) 2)))

Let Over Lambda - 章 5

(defun defunits-chaining (u units prev)
  (if (member u prev)
      (error "~{ ~a~^ depends on~}"
             (cons u prev)))
  (let ((spec (find u units :key #'car)))
    (if (null spec)
        (error "Unknown unit ~a" u)
        (let ((chain (second spec)))
          (if (listp chain)
              (* (car chain)
                 (defunits-chaining
                     (second chain)
                     units
                   (cons u prev)))
              chain)))))

(defmacro! defunits (quantity base-unit &rest units)
  `(defmacro ,(symb 'unit-of- quantity)
       (,g!-unit-value ,g!-unit)
     `(* ,,g!-unit-value
         ,(case ,g!-unit
                ((,base-unit) 1)
                ,@(mapcar (lambda (x)
                            `((,(car x))
                              ,(defunits-chaining
                                (car x)
                                (cons
                                 `(,base-unit 1)
                                 (group units 2))
                                nil)))
                          (group units 2))))))
4

3 に答える 3

12

これはちょっとトリッキーです:

問題: 逆引用符/コンマ式が単純なリストであると想定しています。

次の質問を自問する必要があります。

逆引用符/コンマ式の表現は何ですか?

リストですか?

実際には、完全な表現は指定されていません。ここを参照してください: CLHS: セクション 2.4.6.1 バッククォートに関する注意事項

SBCLを使用しています。これを参照してください:

* (setf *print-pretty* nil)

NIL


* '`(a ,b)

(SB-INT:QUASIQUOTE (A #S(SB-IMPL::COMMA :EXPR B :KIND 0)))

したがって、コンマ式は type の構造体で表されSB-IMPL::COMMAます。SBCL の開発者は、このようなバッククォート リストをプリティ プリンタで出力する必要がある場合に、この表現が役立つと考えていました。

構造を原子として扱うため、flatten内部は見えません...

しかし、これは SBCL の特定の表現です。Clozure CL は別のことを行い、LispWorks は再び別のことを行います。

閉鎖 CL:

? '`(a ,b)
(LIST* 'A (LIST B))

LispWorks:

CL-USER 87 > '`(a ,b)
(SYSTEM::BQ-LIST (QUOTE A) B)

デバッグ

何らかの形flattenで関与していることがわかったので、次のデバッグ手順は次のとおりです。

まず、関数flattenをトレースして、どのデータで呼び出され、何を返すかを確認します。

データが実際に何であるかわからないので、それを行うことができINSPECTます。

SBCL を使用したデバッグ例:

* (defun flatten (x)                                                                                         
    (inspect x)                                                                                              
    (labels ((rec (x acc)                                                                                    
               (cond ((null x) acc)                                                                          
                     ((atom x) (cons x acc))                                                                 
                     (t (rec (car x) (rec (cdr x) acc))))))                                                  
      (rec x nil)))
STYLE-WARNING: redefining COMMON-LISP-USER::FLATTEN in DEFUN

FLATTEN

INSPECT上記の引数データの呼び出し。Common Lisp では、インスペクターは通常、データ構造を対話的に検査できるものです。

flatten例として、バッククォート式で呼び出しています。

* (flatten '`(a ,b))

The object is a proper list of length 2.
0. 0: SB-INT:QUASIQUOTE
1. 1: (A ,B)

インタラクティブなインスペクターにいます。コマンドが利用可能になりました:

> help

help for INSPECT:
  Q, E        -  Quit the inspector.
  <integer>   -  Inspect the numbered slot.
  R           -  Redisplay current inspected object.
  U           -  Move upward/backward to previous inspected object.
  ?, H, Help  -  Show this help.
  <other>     -  Evaluate the input as an expression.
Within the inspector, the special variable SB-EXT:*INSPECTED* is bound
to the current inspected object, so that it can be referred to in
evaluated expressions.

したがって、コマンド1はデータ構造 (ここではリスト) に入ります。

> 1

The object is a proper list of length 2.
0. 0: A
1. 1: ,B

さらに歩きます:

> 1

The object is a STRUCTURE-OBJECT of type SB-IMPL::COMMA.
0. EXPR: B
1. KIND: 0

ここで、インスペクターは、オブジェクトが特定のタイプの構造であることを示しています。それが私たちが知りたかったことです。

コマンドを使用してインスペクターqを終了すると、flatten関数は続行して値を返します。

> q

(SB-INT:QUASIQUOTE A ,B)
于 2015-11-15T21:32:41.660 に答える
2

defmacroを手に入れようとしている人のために!SBCL で作業するには、この問題の一時的な解決策は、再帰的にその内容を平坦化する平坦化手順中に unquote 構造内を手探りすることです。

(defun flatten (x)
  (labels ((flatten-recursively (x flattening-list)
             (cond ((null x) flattening-list)
                   ((eq (type-of x) 'SB-IMPL::COMMA) (flatten-recursively (sb-impl::comma-expr x) flattening-list))
                   ((atom x) (cons x flattening-list))
                   (t (flatten-recursively (car x) (flatten-recursively (cdr x) flattening-list))))))
    (flatten-recursively x nil)))

しかし、これは恐ろしくプラットフォームに依存しています。もっと良い方法を見つけたら、投稿します。

于 2015-11-21T23:43:17.640 に答える
2

誰かがまだこれに興味を持っている場合は、ここに 3 セントを差し上げます。上記の の変更に対する私の異論は、flatten元のようにより自然に役立つ可能性があるということですが、 unquote の表現に関する問題はかなり固有の問題ですdefmacro/g!defmacro/g!機能を使用して何をすべきかを決定するというあまりきれいではない変更を思いつきました。つまり、非 SBCL 実装 ( #-sbcl) を扱う場合は以前と同じように処理を進めますが、SBCL の場合 ( #+sbcl) は構造を掘り下げ、必要に応じsb-impl::commaてそのexpr属性を使用equalpremove-duplicates、シンボルではなく構造を扱うようになったため、 in で使用します。コードは次のとおりです。

(defmacro defmacro/g! (name args &rest body)
  (let ((syms (remove-duplicates
               (remove-if-not #-sbcl #'g!-symbol-p
                              #+sbcl #'(lambda (s)
                                         (and (sb-impl::comma-p s)
                                              (g!-symbol-p (sb-impl::comma-expr s))))
                              (flatten body))
               :test #-sbcl #'eql #+sbcl #'equalp)))
    `(defmacro ,name ,args
       (let ,(mapcar
              (lambda (s)
                `(#-sbcl ,s #+sbcl ,(sb-impl::comma-expr s)
                         (gensym ,(subseq
                                   #-sbcl
                                   (symbol-name s)
                                   #+sbcl
                                   (symbol-name (sb-impl::comma-expr s))
                                   2))))
              syms)
         ,@body))))

SBCL で動作します。私はまだ他の実装で徹底的にテストしていません。

于 2019-01-02T23:26:34.913 に答える