push!

Scheme関数型言語である、と言う説明は充分ではない。 関数型的な性質を主軸にしたマルチパラダイム言語と言うべきだろう。
オブジェクトに破壊的な操作は出来る。 それにより、リスト同士が構造を共有するといったことも有り得たりするので、慣れないと混乱しやすいかもしれない。
実際に躓く例をしばしば見掛けるので、その内のひとつをとりあげてみる。

http://d.hatena.ne.jp/yagiey/20090322/1237738147

ここでは push! という操作が混乱の元になっている。

まず、 (push! lst item) という操作が (set! lst (cons item lst)) と同じ意味であることを意識して手順を追ってみよう。

(define x '(2 3 4))
(push!  x 1)
x ; => (1 2 3 4)

リスト x の構造を図で表すとこうなる。
f:id:SaitoAtsushi:20090323134244p:image
これに push! するのにまずすることは cons セルをアロケートすることだ。
f:id:SaitoAtsushi:20090323134245p:image
アロケートした cons セルの car 部、 cdr 部はそれぞれ「追加した要素(ここでは1)」「元のリスト」ということになる。
f:id:SaitoAtsushi:20090323134246p:image
最後に変数 x に set! したら完了だ。
f:id:SaitoAtsushi:20090323134247p:image
ここまでは躓く要素は特に無いと思う。 次は一旦他の変数に束縛した場合を考える。

(define x '(2 3 4))
(define y x)
(push! y 1)
x ; => (2 3 4)
y ; => (1 2 3 4)

まず、 x をリストに束縛し、同じもので y を束縛する。 このとき x と y は同一のものを指している。
f:id:SaitoAtsushi:20090323134248p:image
y に対して push! すると、 cons セルのアロケートとそのセルの中身までは先の例と変わらない。
f:id:SaitoAtsushi:20090323134249p:image
そして肝心なのは最後の set! である。 束縛の付け替えが起こるのは y に対してだ。
f:id:SaitoAtsushi:20090323134250p:image
x が指す内容は何も変わっていない。
注目すべきは「破壊的操作」には変数の束縛を変える変更と、変数が指すオブジェクトの内容を変える変更があるということだ。
リストの操作で混乱したらとりあえず図を書いてみるのは Lisp 系言語を学ぶ上で有用だと思う。
Document ID: c53568f94b7130d76022f1e794b6e56c