複数の式のリストに展開されるマクロを展開するマクロを定義するマクロ

はじめに

最近 Common Lisp を学んでいます. 演習の題材として,行列計算ライブラリを自分で書いているのですが, ちょっとした問題に遭遇しました.

;;; 行列ベクトル積を計算する
;;; 構造体Aを分解してヘルパー関数 csr-mv に渡している
(defmethod mv ((A csr) v)
  (with-slots (nrow entries rowptr colind) A
    (csr-mv nrow entries rowptr colind v)))

複数の式の並びが重複していて書くのが大変です. csr-mv の設計を見直すのがよいでしょうが, マクロで当座をしのぐことができないか考えてみます. 目標は,マクロを活用して大体次のような感じで書けるようにすることです.

(with-slots (my-macro) A
  (csr-mv (my-macro) v))

検索してみると次のような記事を見つけました [1] [2].

Is it possible to expand a macro into several elements?

I want a macro my-macro which can expand to 1 2 3 rather than (1 2 3), so that

(list (my-macro) 4 5) -> (1 2 3 4 5)

Is this possible?

No, macros cannot expand to more than one value. The typical thing to do when you need a macro to expand to multiple pieces of code is to wrap the return value in a progn.

In your example in the comments, it looks as if you are using macros not as syntactic abstractions, but as cheap-and-cheerful function optimizations, the usual response to that is "please don't do it, it is wrong and doesn't actually do what you want".

ということで,調べた結果残念ながらこの問題は解決不可能ということが分かりました.

問題を曲げる

であれば,問題を曲げて,解決可能な範囲にまで落としこむことはできないでしょうか. たとえば,次のように書くことは可能でしょうか.

(with-expand my-macro (1 2 3)
  `(list ,@my-macro 4 5))     ; -> (1 2 3 4 5)

実はこっちについては可能なのです. 試行錯誤の結果次のようなマクロ定義に至りました.

(defmacro with-expand (symbol expr-list body-expr)
  (with-gensyms (macro-name)
    `(symbol-macrolet ((,symbol ',expr-list))
       (macrolet ((,macro-name () ,body-expr))
         (,macro-name)))))

動作例です.

CL-USER> (with-expand my-macro (1 2 3) `(list ,@my-macro 4 5))
(1 2 3 4 5)
CL-USER> (with-expand args (1 2 3) `(format t "~a,~a,~a,~a,~a,~a~%" ,@args ,@args))
1,2,3,1,2,3
NIL

もう少し洗練したインターフェースにできれば嬉しいですが, 予備検討として一旦ここまでで記事とします.

更新履歴

  • 2024-12-31: 公開

Permanent ID of this document: d37ad09cfddcc6048ee477f3316f6e298477f496

参考文献

[1] SaltyEgg and Joshua Taylor (2013年9月19日). “Is it possible to expand a macro into several elements?”. Stack Overflow. https://stackoverflow.com/q/18886612, (2024年12月31日閲覧).
[2] Vatine (2013年9月19日). Stack Overflow. https://stackoverflow.com/a/18892150, (2024年12月31日閲覧).