« 仕事PCが壊れて悩む | トップページ | イチローの不調に悩む »

2011年9月25日 (日)

Perlの二次元配列で悩む

Perlの機能を確認するには、デバッグモードで起動すると便利だ。LinuxでもWindowsでもコマンドラインから-de0というオプションをつけるとデバッグモードになる。

D:\WORK>perl -de0 Loading DB routines from perl5db.pl version 1.31 Editor support available. Enter h or `h h' for help, or `perldoc perldebug' for more help. main::(-e:1): 0

まず普通の1次元配列を作る。


DB<1> @x=qw(abc def ghi jkl)

Xコマンドで配列の中身を表示させると、当然こうなっている。


DB<2> X x
@x = (
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
)

作った一次元配列@xを@yにコピーする。


DB<3> @y=@x

その中身は当然同じである。


DB<4> X y
@y = (
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
)

コピーした@yの2番目(ゼロから数えて)の要素を書き換える。


DB<5> $y[2]="###"

@xと@yを比べると、@yは変更されているが、当然@xは変更されていない。そりゃあたりまえだ。


DB<6> X x y
@y = (
0 'abc'
1 'def'
2 '###'
3 'jkl'
)
@x = (
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
)

では、これが二次元配列になるとどうか?

同じように二次元配列@xを作って@yにコピーし、@yの要素を変更してみる。

DB<7> @x=([qw(abc def ghi jkl mn)],[qw(opq rst uvw xyz)])
DB<8> X x
@x = (
0 ARRAY(0x1d54ee0) 0 'abc' 1 'def' 2 'ghi' 3 'jkl' 4 'mn' 1 ARRAY(0x1d3ab90) 0 'opq' 1 'rst' 2 'uvw' 3 'xyz' )

DB<9> @y=@x

DB<10> X y
@y = (
0 ARRAY(0x1d54ee0)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d3ab90)
0 'opq'
1 'rst'
2 'uvw'
3 'xyz'
)
DB<11> $y[1][3]="###"

DB<12> X y
@y = (
0 ARRAY(0x1d54ee0)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d3ab90)
0 'opq'
1 'rst'
2 'uvw'
3 '###'
)

すると、@xも変わってしまっているではないか!?


DB<13> X x
@x = (
0 ARRAY(0x1d54ee0)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d3ab90)
0 'opq'
1 'rst'
2 'uvw'
3 '###'
)

なんとこれはPerlの仕様なのである。Perlの多次元配列は「参照」で実装されているので、コピーしたつもりになっていても実際には「参照のコピー」なので実体は同じものをさしており、コピーしたものを変更するとオリジナルも変更されてしまう。これは困った。

ループを回して新しい配列に代入すればもちろん実体コピーができるのだが、できればそんなことはしたくない。なにかうまい手は無いか?

二次元配列の一つ一つについてコピーすればいいのだから、map関数が使えそうな気がする。
これでどうだろう?


DB<14> @y=map @{$_},@x

DB<15> X y
@y = (
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
5 'opq'
6 'rst'
7 'uvw'
8 '###'
)

ありゃりゃ、配列が一次元になってしまった。しかし方向性としては間違っていないみたいだぞ。
これでどうだ?


DB<16> @y=map @{$_},\@x

DB<17> X y
@y = (
0 ARRAY(0x1d54ee0)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d3ab90)
0 'opq'
1 'rst'
2 'uvw'
3 '###'
)

これは一見うまくいったように見えるが、表示されているアドレスを見ると@xと同じで、つまりやはり参照がコピーされていることが分かる。これだとやはりコピーを変更するとオリジナルも変わってしまう。

map関数の中で配列になるよう[]でくくってみよう。


DB<18> @y=map [@{$_}],\@x

DB<19> X y
@y = (
0 ARRAY(0x1d5b370)
0 ARRAY(0x1d54ee0)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d3ab90)
0 'opq'
1 'rst'
2 'uvw'
3 '###'
)

うむぅ、配列の次元がひとつ上がってしまった。これはまずい。オリジナルを参照しているからいけないのか?


DB<20> @y=map [@{$_}],@x

DB<21> X y
@y = (
0 ARRAY(0x1d5b364)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d5b2c8)
0 'opq'
1 'rst'
2 'uvw'
3 '###'
)

おお、これは行けそうな気がする。コピーの要素を変更してみよう。


DB<22> $y[1][3]="!!!"

さぁどうだっ!?


DB<23> X x y
@y = (
0 ARRAY(0x1d5b364)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d5b2c8)
0 'opq'
1 'rst'
2 'uvw'
3 '!!!'
)
@x = (
0 ARRAY(0x1d54ee0)
0 'abc'
1 'def'
2 'ghi'
3 'jkl'
4 'mn'
1 ARRAY(0x1d3ab90)
0 'opq'
1 'rst'
2 'uvw'
3 '###'
)

よしよし、これでいいみたいだな。

|

« 仕事PCが壊れて悩む | トップページ | イチローの不調に悩む »

プログラミング」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: Perlの二次元配列で悩む:

« 仕事PCが壊れて悩む | トップページ | イチローの不調に悩む »