Common Lispの系譜を継ぐマイコン上のuLispをラズパイPico2上で練習中。前回はCommon Lisp上の「マクロ」と異なるuLispでの実装が、実装に関わりなく同様な動作をすることを観察。しかし今回はCommon Lisp上とuLisp上で反応が違うんでないかい、という「元マクロ」どもを観察します。
※Lispと一緒 投稿順 index はこちら
※実機確認は Raspberry Pi Pico2で行ってます。
※使用させていただいとります uLisp のバージョンは 4.6b (Arm用)です。
※uLispとCommon Lispとの動作比較のために使わせていただいている処理系は以下です。
SBCL 2.2.2 (SBCL = Steel Bank Common Lisp )
今回、動作を確認してみるマクロであったものども
以下5個のCommon Lisp上ではマクロとして実装されているハズのものども、uLisp上ではSpecial formsとして実装されているものを実機上で動作確認してみます。いずれも「データの書き換え」に使えそうなものどもです。
結論を先に言うと、前の3個、「インプレースでの書き替え」を行うものについては、Common LispとuLispでは挙動が異なります。push、popは大丈夫そうだけれども。
-
- incf
- decf
- setf
- pop
- push
ただ、動作の違いは、マクロかそうでないか、という違いというより、「インプレースでの書き替え」についての仕組みが異なるためみたい。
incf、decf
incfは、第1引き数の「場所」にある数、整数でも浮動小数でもかまわん、を「インクリメント」してくれるCommon Lispではマクロ、uLispではスペシャルフォームです。通常引数1個だと、インクリメントは+1の意味ですが、オプショナルな第2引数を与えることで+第2引数の値、のように作用を変化させることができます。これが通常の+(プラス)関数と異なるのは、+1(あるいは+第2引数)した値を返り値として返してくれるだけでなく、「場所」の値そのものを書き替えてしまう(副作用ってやつだね)ところです。
decfは、incfと対になり。デクリメントしてくれるもの。-1あるいは第2引数使って‐第2引数という操作です。副作用の件は同じ。
リスト(1 2 3 4)のcadrをとったらリストの先頭から2番目の「2」です。それをincfすると、3が返るだけでなく、元のリストxの2番目の要素も書き換わってます。またdecfすると2が返り、やはり元のリストxの2番目の要素も書き換わってます。便利だね~。
しかし、上と同じことを uLisp 上で実行しようとするとこんなんです。
なんやら「場所」が悪い、と。なんでじゃ?
試みに、書き替える場所をリストのcadrとかでなく、単純な変数にしてみるとこんな感じ。
普通にincf、decfできますやん。どうも uLisp の場合、「場所」の取り扱いはCommon Lispと同様とはいかないようです。
setf
setfは、第1引き数の「場所」に対して第2引数の値を書き込んでくれるものです。上記の例からして差異がアリ。
先ほどのリストxの2番目の要素を999で書き替えてます。返り値999だけでなく、xの書き替え「副作用」も期待通り。
uLispではそうは問屋が卸してくれないので、例はこんな感じ。トホホ。
pop、push
popはリストの先頭要素を取り出して先頭要素を戻り値として返すだけでなく、元のリストから先頭要素を取りのぞく「副作用」を施すもの。逆にpushは、リストの先頭にある値を挿入したリストを返してくれるだけでなく、元のリストも書き替えてくれるもの。
これらも副作用目的のものと言えますが、フツーにリスト操作しているためか、Common LispとuLispでの挙動の差は観察できませなんだ。
ううむ、挙動が同じ奴らも違う奴らもまざっているのね。。。