単純に次ぎのコード、
(define (foo) '(a b c)) (foo) ==> (a b c)
fooはどってことのない関数だが、実は、この書き方はとても危ない。
プログラムのどこかで、fooの出力を破壊的に変更してしまうと、
(reverse! (foo)) ==> (c b a) (foo) ==> (a)
もはや、関数fooは (a b c)を返さなくなる。
vectorも同じだ。
(define (foo) '#(a b c)) (vector-set! (foo) 0 'aa) (foo) ==> #(aa b c)
準クォートを使ったこれもだめだ
(define (foo x) `(,x a b c)) (foo 1) ==> (1 a b c) (reverse! (foo 2)) ==> (c b a 2) (foo 1) ==> (1 a 2)
しかしこれは大丈夫 (少なくともgoshでは)
(define (foo x) `(a b c ,x)) (foo 1) ==> (a b c 1) (reverse! (foo 2)) ==> (2 c b a) (foo 1) ==> (a b c 1)
無難な書き方はこうかな?
(define (foo x) (append `(,x a b c) '())) (foo 1) ==> (1 a b c ) (reverse! (foo 2)) ==> (c b a 2) (foo 1) ==> (1 a b c )
まとめ
- クォートで作ったリストやベクターは、静的にアロケートされている可能性がある
- クォートで作ったリストやベクターに対し、直接に破壊操作を行わない。
- 知らない関数の出力を破壊する時は、 前もってコピーしたものを破壊するのが無難。
しかしこれに慣れると、他の言語で同様なことを書くのに躊躇してしまいそうだ。
例えば、rubyだと
def foo ; return [1,2,3] ; end foo.reverse! => [3, 2, 1] foo => [1, 2, 3]で全然問題ないのに、
def foo ; return [1,2,3].clone ; endと書いてしまうとか......
0 件のコメント:
コメントを投稿