第4回 Perlはメモリについて意識しないでいいようにできている

Joel on Software

Joel on Software

Joel on Software(上記)でも述べられているのですが、プログラマオブジェクト指向の言語ではなくメモリを自動で「確保&解放」してくれる言語により力を得ることができました。


そうです。Perlを使えばプログラマはメモリを意識しないで、快適なプログラミングが行えます(といってもメモリのことを意識しないと楽しいプログラムなんてどんな言語でも書けないと思いますが)。


さて、プログラムは「メモリ」の上で動いてるわけですが、僕はこの「メモリ」という言い方が好きではありません。「メモリ」というと「記憶する何か」という意味合いでとれてしまいます。そうするとハードディスクもUSBメモリも、僕の脳みそも「メモリ」になってしまいます。そこで僕は「メインメモリ」という言い方の方が好きです。


余談は、それくらいとして、Perlでは「メインメモリ」をあんまり意識しないでいいようにできてます。
C言語だと、mallocなんかで頑張らないといけないですし、Java, Ruby, JavaScript なんかだと皆大好き 「new」を使うわけです。


Perlのメインメモリを意識しないでいいと言った内容を実感できるのは、「リファレンス」とか「無名配列」「無名ハッシュ」「無名関数」(これらを僕は無名シリーズと勝手に呼んでいます)を理解してからだと思うのですが、今回は、単なる配列でもってその機能を紹介したいと思います。


では、配列を作ってみましょう。

use strict;
my @array = 1..3;

それでは、配列の中身を表示して確認してみましょう。
確認の仕方としては、

print "$_", "\n" for @array;

ワンライナーで書いてしまえばいいですね。

結果:

1
2
3


個人的にはこっちを使いますが、

use strict;
use Data::Dumper;   # ダンパー 超便利

my @array = 1..3;
print Dumper \@array;

結果:

$VAR1 = [
          1,
          2,
          3
        ];

まあ, print Dumper で、Ruby の p, PHPのprint_r みたいなものです。

といってもprint Dumperの方を理解するには、「関数の引数の受け取り方」と「リファレンス」の理解が必要なので、今回はワンライナーの方を使っていきましょう。


では、配列の中身を確認する方法がわかったので、配列を自在に扱っていきましょう。

use strict;

my @array = 1..3;
push @array, 4;   # pushを使う

print "$_", "\n" for @array;

結果:

1
2
3
4

ほら、push を使えば、配列の最後に要素を追加することができました。これはC言語の配列では不可能だと思います。C言語でこの機能を使いたいなら自分で連結リストなりをポインタを使って頑張って書く必要があります。

pushとかこういった配列への便利な操作(メインメモリを意識しないでいいようになっている事がポイント)は結構色んな言語が実装しています。

p Array.new([1,2,3]).push(4);

結果:

[1, 2, 3, 4]

Rubyだとこんな感じですね(※ めんどくさいのでワンライナーで書きました)。


shift というのもあって、

use strict;

my @array = 1..3;
my $c     = shift @array;     # shift

print "$_", "\n" for @array;

print '$cは' . "$cです", "\n";

結果:

2
3
$cは1です

こんな感じで、第1番目の要素を配列から取得できます。
このとき配列からその要素は削除されます。つまり、配列の大きさが縮んだのです。

こんな風に、Perlの配列は他の多くの言語の配列と同じように伸縮自在なのです!!!

だが、しかし!!! C言語は、配列の大きさを定数としてプログラムの最初の方に記載しておく場合が多い。


これに影響を受けてしまって、

use strict;
my @array  = 1..3;
my $length = @array;

for(my $i=0; $i<$length; $i++){
  print "$array[$i]", "\n";
}

と書いてしまう人のなんと多いことか!!!!!



配列は伸縮自在なのだ!!!!! それを忘れるな!!!!!!!!!
push @array, 4 としたときに、 $lengthはどうするつもりだ!!!!!!!




配列の大きさなど、変数に入れる必要はない。forが勝ってに配列のぶんだけ繰り返してくれるのだ。

use strict;
my @array  = 1..3;

for(@array){
  print "$_", "\n";
}

結果:

1
2
3

まさに、この書き方は先ほどから書いている print "$_", "\n" for @array のことである。

Rubyだとさしずめ、こんな感じだ。

Array.new([1,2,3]).each{ |_| p _};

このように、配列が伸縮自在な言語だと、配列の大きさぶんだけ勝手にループしてくれるモノが多い。
そしてこれは非常に便利だ。JavaScriptもArrayのprototypeをごにょごにょやって、eachを作る人もいる(ライブラリとかもある)。


今回、Rubyのeachを紹介したが、このeachはPerlのforとは似ているようで全然違う。
アドバンテージな内容になるが、RubyのeachはJavaScriptのprototypeをごにょごにょやるのに似ている。
JavaScriptのそれは、each関数に処理してほしい関数を渡すのだ(高階関数という)。Rubyはこの高階関数機能を言語それ自体に組み込んでしまっているのである。この話は、今回のPerlの話とはあまり関係なくなってしまうが、非常におもしろいところなので勉強するとよいと思う。



とまあ、そんな感じで、配列を回すなら、 for に任せた方がよい。大きさを変数で持とうなどナンセンスもいいところだ。
どうしても配列のインデックスを回るたびに$iに入れたいんだというなら、以下のような方法をお勧めする。

use strict;
my @array  = 1..3;

for(my $i=0; $i<int @array; $i++){
  print "$array[$i]", "\n";
}

このように @arrayをスカラーで評価してやればよいのだ。

しかしこれも, forの ( ) の中がうるさい気がする。

つぎのようにしてもいい

use strict;
my @array  = 1..3;

for my $i(0..$#array){
  print "$array[$i]", "\n";
}

なんだか とってもPerl知ってるな! という気分になる。


だが!! どちらにせよこれらは最後の手段だ!!!!
ふつうに、

for(@array){
  print "$_", "\n";
}

forに配列の大きさぶんだけ回してもらえばいいのである!!!!


ということで、本当は、ただの配列の話でメインメモリの「確保&解放」の話をもってくるのは忍びなかったのですが(本当はリファレンスとか無名シリーズのところで出したい)、今回お話させてもらいました〜。


ということでまた次回お会いしましょう〜