3

リストをマクロに渡そうとしています。例:

(defmacro print-lst (lst)
  `(progn
     ,@(mapcar #'(lambda (x) `(print ,x)) lst)))
(let ((lst '(1 2 3)))
      (print-lst lst))

エラーが発生しました:「値LSTはタイプLSTではありません」。

だから、私の質問は、このコードの何が問題になっているのか、そしてリストをマクロに渡す方法ですか?

4

3 に答える 3

8

これを通常の関数ではなくマクロとして定義する理由がわかりませんが、問題はマクロが引数を評価しないことです。レキシカル変数の名前を指定すると'LST、バインドされた値ではなく、名前 ( ) だけが表示されます。'LSTシンボルがリストではなく、したがって の有効な 2 番目の引数ではないことを (正しく) 訴えていMAPCARます。

として呼び出すことも(print-lst (1 2 3))できますが、マクロを使用せずに実行することもできます(mapc #'print lst)

于 2012-11-02T14:08:53.060 に答える
4

マクロでやろうとしているのは、リテラル リストを展開することです。

マクロ引数は評価されません。したがって、print-lst実際にはlst、変数にバインドされたリストではなく、シンボルを受け取ります。

それを認識しprint-lstてリテラル リストを指定するか、マクロ引数を評価するコードを生成することができます。

(defmacro print-lst (lst)
  (let ((item (gensym)))
    ;; Macros usually make sure that expanded arguments are
    ;; evaluated only once and in left-to-right order.
    ;; 
    ;; In this case, we only have one argument and we only evaluate it once.
    `(dolist (,item ,lst)
       (print ,item))))

これは明らかにマクロの良い例ではありませんが、関数の方がはるかに優れています。

(defun print-lst (lst)
  (dolist (item lst)
    (print item)))

の呼び出しをインラインprint-lst化する場合は、実装のドキュメントを参照して、 に注意を払っているかどうかを確認してください(declaim (inline print-lst))

別のオプションは、関数を補完するコンパイラ マクロを使用して、コンパイル時に引数の評価が既知の値であるインライン呼び出しを行うことですが、実装がコンパイラ マクロに注意を払っているかどうかをもう一度確認してください。

(define-compiler-macro print-lst (&whole form lst &environment env)
  (declare (ignorable env))
  ;; Some implementations have an eval function that takes an environment.
  ;; Since that's not standard Common Lisp, we don't use it in constantp.
  (if (constantp lst)
      `(progn
         ,@(mapcar #'(lambda (item)
                       `(print ,item))
                   (eval lst)))
      ;; Return the original form to state you didn't transform code.
      form))
于 2012-11-02T22:57:21.953 に答える
1

あなたのコードは不必要にマクロを使用しました。実際には、上記の誰かのように eval (mapcar #'print '(1 2 3)) を実行できます。リスト引用符なしで (print-lst (1 2 3)) を使用することもできますが、引数を使用して関数を呼び出す一般的な慣行に適合しないため、お勧めしません。

全体として、マクロを呼び出すときに、評価されると予想される場合は、評価コンマ「,」なしでシンボルを引用しないでください。これは、文字通りマクロ テンプレートに置き換えられるためです。

例えば

(setq a 1)

;;A won't be evaluated without the comma
(defmacro foo (x) `(progn ,(print x))
(foo 'a) ;;=> 'A (A is returned since (print 'a) is evaluated)
(foo a) ;;=> A (1 is returned since (print a) is evaluated)

;;A is evaluated
(defmacro bar (x) `(print ,x))
(bar 'a) ;;=> A
(bar a) ;;=> 1

あなたのコード:

(defmacro print-lst (lst)                                                                                    
  `(progn                                                                                                    
     ,@(mapcar #'(lambda (x) `(print ,x)) lst)))

「mapcar」フォームは全体として評価されますが、「lst」は渡されたものにのみ置き換えられます。

問題を解決するために、マクロの代わりに関数を定義できます。

(defun print-lst (lst)
  (mapcar #'eval
    (mapcar #'(lambda (x) `(print ,x)) lst)))

それで:

(let ((lst '(1 2 3)))
  (print-lst lst))

;;=>
1
2
3
(1 2 3)

タイトルの質問に興味を持った場合は、これに関する私の別の回答を見ることができます。

elisp のリストに「または」を適用するにはどうすればよいですか

于 2016-01-22T12:12:09.917 に答える