はじめに
この連載ではCommon LispのLOOPマクロをサンプルを使って紹介する。
LOOPマクロは (cl-loopを用いることで) Emacs Lispでも利用可能である。
【Common Lisp: loopマクロ用法抄より引用】
Grahamの ANSI Common Lisp では嫌われていて碌に説明のないloopマクロ。一方、 実践Common Lisp では対照的に好んで用いられていて、全編に渡って頻繁に使われている。しかしloopマクロは難しいという意識があるのかその説明は第22章とかなり後回しにされており、ちぐはぐな感を受ける。ここでは、 黒帯のためのLOOP という題のつけられたその章で解説されているloopマクロの用法を整理してみた。
ANSI Common Lisp での黒魔術扱いに敬遠していたloopマクロだったが、こうして整理してみるとそれほど難しく考えずとも便利に使うことができそうだ。
実践Common Lisp
作者: Peter Seibel,佐野匡俊,水丸淳,園城雅之,金子祐介
出版社/メーカー: オーム社
発売日: 2008/07/26
メディア: 単行本(ソフトカバー)
LOOPのパーツ(黒帯のためのLOOPより)
LOOPの中では次のようなことができる。
- 数値的な変数や多様なデータ構造にわたる変数を更新していく
- ループしている間に見える値を収集(collect)、計数(count)、合計(sum)、最小化(minimize)、最大化(maximize)する
- 任意のLisp式を実行する
- いつループを終了するかを決定する
- それらを条件付きで実行する
上記に加えて、LOOPは以下のようなシンタックスを提供する。
- ループ内で使用するローカル変数の生成
- ループの前後に実行する任意のLisp式の指定
サンプル (GNU Emacs 28.2 で動作確認しています)
また、@javacommons さんの以下の記事内の xpand-macro/xpand を用いてマクロ展開結果を取得しています。
今回は…
今回は、LOOP マクロに関する Emacs Lisp と Common Lisp の非互換性について紹介します。
ベクターのリードシンタックスの違い
- 厳密には、LOOPマクロ自体の違いではないですが…
CLISP> (loop for i across #(1 2 3) do (print i))
1
2
3
⇒NIL
(cl-loop for i across [1 2 3] do (print i))
|
|
v
(let* ((--cl-vec-- [1 2 3])
(--cl-idx-- -1)
(i nil))
(while (and (setq --cl-idx-- (1+ --cl-idx--))
(< --cl-idx-- (length --cl-vec--)))
(setq i (aref --cl-vec-- --cl-idx--))
(print i))
nil)
1
2
3
nil
値の累積(accumulation)に関する違い
- Emacs Lisp では、以下のように concat (文字列の結合) accumulation が使えます。
(cl-loop for str in '("A" "B" "C")
for sep = "" then ","
concat sep
concat str)
|
|
v
(let* ((--cl-var--_1 '("A" "B" "C"))
(str nil)
(sep nil)
(--cl-var--_2 "")
(--cl-var--_3 t))
(while (consp --cl-var--_1)
(setq str (car --cl-var--_1))
(setq sep (if --cl-var--_3 "" ","))
(setq --cl-var--_2 (concat --cl-var--_2 sep))
(setq --cl-var--_2 (concat --cl-var--_2 str))
(setq --cl-var--_1 (cdr --cl-var--_1))
(setq --cl-var--_3 nil))
--cl-var--_2)
"A,B,C"
最後に
他にも非互換等ありましたらコメント等でお願いします。