#:g1: sloopの紹介

Posted 2014-04-16 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の107日目です。

sloopとはなにか

 sloopは、William Schelter氏作が1985年頃に作製した繰り返しマクロのライブラリです。
基本的な機能はMITのLOOP互換になっています。

パッケージ情報

パッケージ名sloop
参考サイトPackage: lang/lisp/code/iter/loop/sloop/

インストール方法

 上記CMUのAIレポジトリからソースをダウンロードして動かしてみましょう。
AIレポジトリのソースは改変禁止と書いてありますが、GCLのソースはGPLなので改変して再配布する際にはGCLのものが良いかと思います〈GCL版は若干の修正がされています〉

試してみる

 1984年の最初のCommon Lispでは、LOOPは単純な無限ループの機能のみとなっていました。
現在ANSI Common Lispに含まれているLOOPの源流のMIT系LispマシンのLOOPは、1980年位からLispマシン上で利用されていて、一定の支持は集めていたようですが、Common Lispにそのまま導入するかどうかはCLtL1の段階では見送られたというところでした。

 CLtL1にリッチな繰り返し構文が含まれなかったお蔭で1980年代後半に結局Lispマシン系のLOOPがCommon Lispに採用されるまでの間に、色々な繰り返し構文や、LOOP互換構文が登場しました。
sloopもそんなものの一つで、CLtL1なKCLやAKCLで専ら利用されたようです。
William Schelter氏は、AKCLの開発者だったようで、AKCL〜GCLでも結構な量のコードがsloopを利用して記述されていたりします。

 基本的な機能はANSI Common LispのLOOPと同様です。

(let ((lis '(1 3 4 2 1 6 7 3 0 22)))
  (sloop :for (i a) :on lis :by 'cddr
         :when (oddp i)
         :collecting i
         :else
         :append (sloop :for j :below a :collecting j)))
;=>  (1 0 1 1 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21)

(let ((n 5)) (sloop :for i :below n :for a :below (* 2 n) :unless (oddp i) :sum i :else :sum (sloop :for j :below a :sum j))) ;=> 9 (let ((lis '(1 3 4 2 1 6 7 3 0 22))) (sloop :for (i a) :on lis :by 'cddr :when (oddp i) :sum i :and :count i :else :sum (sloop :for j :below a :sum j))) ;=> 244

(let ((lis '(1 3 4 2 1 6 7 3 0 22))) (sloop for (a i) on lis by 'cddr when (not (zerop a)) nconc (list (* 2 a) i) else nconc (list 3 i) while (not (eql i 6)))) ;=> (2 3 8 2 2 6)

(let ((lis '(1 3 4 2 1 6 7 3 0 22))) (sloop for (a i) on lis by 'cddr when (not (zerop a)) nconc (list (* 2 a) i) into tem else nconc (list 3 i) into tel finally (loop-return (append tem tel)))) ;=> (2 3 8 2 2 6 14 3 3 22)

 以上のように基本的なところは同じですが、パッケージや、ハッシュテーブル等の利用では少し書き方が違ってきます。

(let ((table (make-hash-table)))
  (setf (gethash 3 table) 11)
  (setf (gethash 11 table) 7)
  (sloop for (key elt) in-table table sum elt into tot sum key into keys
	 finally (loop-return (list keys tot))))
;=>  (14 18)

(sloop :for sym in-package :cl :when (eql sym 'cons) :do (loop-return sym)) ;=> CONS

in-packageや、in-tableで指定します。ANSI CLのLOOPより分かりやすい気もしないでもありません。

(sloop :for i :from 0 :to 99
       :declare (fixnum i)
       :sum i)
;=>  4950

さらに型宣言はdeclareキーワードで指定します。

 加えて独自の拡張機能があります。

sloop
(sloop :for i :below 5
       sloop (for j  :to i :collecting (list i j)))
;=>  ((0 0) (1 0) (1 1) (2 0) (2 1) (2 2) (3 0) (3 1) (3 2) (3 3) (4 0) (4 1) (4 2) (4 3) (4 4))

入れ子にする際に(sloop ... (sloop forではなく、(sloop ... sloop (forと書けるようにするもの。すっきりするようなしないような。

in-carefully
(sloop for e in-carefully '(1 2 3 . 4) collect e)
;=>  (1 2 3)

NIL終端でないリストは終端を無視して扱うという指定

in-fringe
(sloop :for x in-fringe '(1 2 (3 (4 5) . *) 8 1 2)
       :collect x)
;=>  (1 2 3 4 5 * 8 1 2)

リストをflattenしてくれるもの

averaging
(sloop :for e :from 1 :to 99 :averaging e)
;=>  50.0

平均を算出

collate
(sloop for v in-fringe '(1  (7 3 (2 . 2) 9 (4 11)) 5 . 3)
       when (oddp v)
       collate v)
;=>  (1 3 5 7 9 11)
(sloop repeat 15 collate (random 2))
;=>  (0 1)

昇順にソートしつつ重複も削除するcollect

 ユーザーも定義することが可能でsloop:def-loop-collect、sloop:def-loop-map、 sloop:def-loop-forあたりで定義が可能です。上記の拡張キーワードはこれらで定義されているので独自に定義する場合には参考になると思います。

まとめ

 今回は、sloopを紹介してみました。
GCLのソースを眺めていて、SLOOPという見慣れない物が大量に使われているのを発見したので紹介してみました。

comments powered by Disqus