mbe 20151126.334(in MELPA)
Macros by Example

概要

mbe.elSchemeパターンマッチ のようなことができます。

SRFI 46 / R6RS スタイルのパターンが使えます。

(<pattern> ...)
(<pattern> <pattern> ... . <pattern>)
(<pattern> ... <pattern> <ellipsis>)
(<pattern> ... <ellipsis> <pattern> ...)
(<pattern> ... <ellipsis> <pattern> ... . <pattern>)

インストール

パッケージシステムを初めて使う人は
以下の設定を ~/.emacs.d/init.el の
先頭に加えてください。

(package-initialize)
(setq package-archives
      '(("gnu" . "http://elpa.gnu.org/packages/")
        ("melpa" . "http://melpa.org/packages/")
        ("org" . "http://orgmode.org/elpa/")))

初めてmbeを使う方は
以下のコマンドを実行します。

M-x package-install mbe

アップグレードする方は、
以下のコマンドでアップグレードしてください。
そのためにはpackage-utilsパッケージが必要です。

M-x package-install package-utils (初めてアップグレードする場合のみ)
M-x package-utils-upgrade-by-name mbe

パターンマッチ結果をローカル変数にするmbe-bind

パターンマッチを行うマクロは mbe-bind です。

(mbe-bind Patパターン
  値
  フォーム...)

の書式になります。

使用例 141222083004.el(以下のコードと同一)

;;; これは単純なパターンでaとbにはそれぞれ1と2が入る
(mbe-bind
 (a b)
 '(1 2)
 a                                    ; => 1
 b                                    ; => 2
 )
;;; シンボル...は直前のパターンの繰り返し
;;; b ...でリストの残りを表す
(mbe-bind
 (a b ...)
 '(1 2 3 4 5)
 a                                    ; => 1
 b                                    ; => (2 3 4 5)
 )
;;; パターン (a b) の繰り返しを表す暗黙のループ
(mbe-bind
 ((a b) ...)
 '((a 1) (b 2) (c 3))
 a                                      ; => (a b c)
 b                                      ; => (1 2 3)
 )
;;; ネストした...
;;; aは最初の要素、bは残りの要素になる
(mbe-bind
 ((a b ...) ...)
 '((a 1 "alpha" "beta") (b 2) (c 3 "gamma"))
 a                           ; => (a b c)
 b                           ; => ((1 "alpha" "beta") (2) (3 "gamma"))
 )
;;; 最後の3 2 1がマッチしている
(mbe-bind
 (a b c ... 3 2 1)
 '(7 6 5 4 3 2 1)
 a                                      ; => 7
 b                                      ; => 6
 c                                      ; => (5 4)
 )
;;; dとeは最後の2要素になる
(mbe-bind
 (a b c ... d e)
 '(1 2 3 4 5 6 7 8 9)
 a                                      ; => 1
 b                                      ; => 2
 c                                      ; => (3 4 5 6 7)
 d                                      ; => 8
 e                                      ; => 9
 )
;;; リストの最後のコンスセルのcdrはnilなのでfはnil
(mbe-bind
 (a b c ... d e . f)
 '(1 2 3 4 5 6 7 8 9)
 a                                      ; => 1
 b                                      ; => 2
 c                                      ; => (3 4 5 6 7)
 d                                      ; => 8
 e                                      ; => 9
 f                                      ; => nil
 )
;;; 最後のコンスセルが9
(mbe-bind
 (a b c ... d e . f)
 '(1 2 3 4 5 6 7 8 . 9)
 a                                      ; => 1
 b                                      ; => 2
 c                                      ; => (3 4 5 6)
 d                                      ; => 7
 e                                      ; => 8
 f                                      ; => 9
 )

パターンマッチを使ってマクロを記述する

mbe-defrules マクロはSchemeの syntax-rules
syntax-case のような形でマクロを定義します。

パターンマッチの方法はmbe-bindと同じです。

incfを定義 141222084410.el(以下のコードと同一)

(mbe-defrules my-incf
  ;; 引数が1つのときは1足す
  ((var) (setq var (+ var 1)))
  ;; 引数が2つのときはby足す
  ((var by) (setq var (+ var by))))
(let ((a 0) (b 0))
  (incf a)                              ; => 1
  (incf a 10)                           ; => 11
  a                                     ; => 11
  (my-incf b)                           ; => 1
  (my-incf b 10)                        ; => 11
  b                                     ; => 11
  )

pushを定義 141222084700.el(以下のコードと同一)

;;; 冗長な記法
(mbe-defrules my-push1
  ((newelt place) (setq place (cons newelt place))))
;;; 引数の形式が1つなのでシンプルな記法でよい
(mbe-defrule my-push2 (newelt place)
  (setq place (cons newelt place)))
(let (x y z)
  (push 1 x)                            ; => (1)
  x                                     ; => (1)
  (my-push1 1 y)                        ; => (1)
  y                                     ; => (1)
  (my-push2 1 z)                        ; => (1)
  z                                     ; => (1)
  )

letを定義 141222084905.el(以下のコードと同一)

(mbe-defrule mylet (((var val) ...) body ...)
  (funcall (lambda (var ...) body ...) val ...))
(mylet ((a 1) (b 2))
  a                               ; => 1
  b                               ; => 2
  (+ a b))                        ; => 3


本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。