Perl Hackers Hub

第72回 初学者に伝えたい Perl学習の勘所 ―勉強会を10年運営して培ったノウハウ(2)

この記事を読むのに必要な時間:およそ 3 分

前回の(1)こちらから。

よくある質問への回答

ここでは,Perl入学式でよく寄せられる質問と,それに対する回答を紹介します。

変数名の左にある記号がコロコロ変わって理解できない

Perlには,変数の種類によって変数名の前に付くシジルと呼ばれる記号があります。スカラは$配列は@ハッシュは%がシジルとして変数名の前に付きます。同じ変数名であっても,異なるシジルが付いていれば別の変数として扱われます。

シジルが$のスカラは,1つの値を保持する変数です。この値は,数値,文字列,そして後述するリファレンスのいずれかです。スカラが保持可能な値はスカラ値と呼ばれます。

シジルが@の配列とシジルが%のハッシュは,複数のスカラ値を要素として含む変数です。配列やハッシュの1要素であるスカラ値にアクセスするときは,シジルが$に変化します。サブルーチンの戻り値として配列を受け取るときは,シジルが@の配列に代入します。ハッシュリファレンスをデリファレンスしたものは,シジルが%のハッシュに代入します。

自分が操作したい対象を意識できると理解が進み,適切なシジルを選択できます。

配列とハッシュを使い分けられない

初学者であっても,スカラ値を格納するだけのスカラの理解は容易です。しかし,配列とハッシュは,ともに要素の集合を扱う用途のため,配列を使うべき場面でハッシュを使う(あるいはその逆)など,すぐに理解して使いこなすのは難しいようです。

配列は,要素を順番に処理する場面で活用します。

array_sample.pl
# 配列によるデータ管理例:出席番号順生徒名簿
my @members = ('Alice', 'Bob', 'Carol', 'Dave');
# 順番に表示する
for my $member (@members) {
    print "name: $member\n";
}

ハッシュは,要素に名前を付けて,必要な要素を名前で取り出せると便利な場面で活用します。そして,配列とは異なり順不同です。

hash_sample.pl
# ハッシュによるデータ管理例:テストの成績管理
my %score = (Japanese => 75, English => 80);
# 英語の点数を表示
print "English score: $score{English}\n";

配列とハッシュそれぞれの特徴を把握すると,正確に使い分けることができます。

初歩的文法を理解できているか不安

15年ほど前に話題になったFizzBuzz問題を解いてみましょう。

1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と,5の倍数のときは「Buzz」とプリントし,3と5両方の倍数の場合には「FizzBuzz」とプリントすること。

─⁠─ Jef Atwood著/青木靖訳どうしてプログラマに・プログラムが書けないのか?⁠ 2007年

この問題を解くためには,順次処理,変数,配列,条件分岐if文⁠⁠,論理演算子,繰り返しfor文)の適切な組み合わせが必要です。

fizzbuzz.pl
# 配列の使用
my @numbers = (1 .. 100);

# for文による繰り返し処理。変数へ値の格納
for my $num (@numbers) {

    # if文による条件分岐処理
    if ( $num % 3 == 0 && $num % 5 == 0 ) {
        print 'FizzBuzz' . "\n";
        } elsif ( $num % 3 == 0 ) {
        print 'Fizz' . "\n";
    } elsif ( $num % 5 == 0 ) {
        print 'Buzz' . "\n";
    } else {
        print $num . "\n";
    }
}

このFizzBuzz問題をスムーズに解ければ,初歩的文法の理解は十分と言えます。

リファレンスを使う理由がわからない

受講生から,リファレンスを使う理由がわからないという意見がよく寄せられます。使う理由がわからないから使わない,使わないから理解が進まないといった悪循環を繰り返し,結果的にPerlの学習を挫折する人もいます。

Perlのスカラには1つの値しか代入できません。配列やハッシュの要素として,別の配列やハッシュ全体を直接格納することはできません。

リファレンスは,実データがある場所を指し示すスカラ値です。配列やハッシュのリファレンスは,スカラに代入したり,配列やハッシュの1要素として格納できます。これを利用すれば,配列とハッシュが入れ子となる複雑なデータ構造を扱えます。

data_struct.pl
# リファレンスを利用し,複雑なデータ構造を扱う
my %person1 = (
    name => 'xtetsuji',
    mail => 'xtetsuji@example.com'
);
my %person2 = (
    name => 'sironekotoro',
    mail => 'sironekotoro@example.com'
);
my %person3 = (
    name => 'tomcha',
    mail => 'tomcha@example.com'
);
my @people = ( \%person1, \%person2, \%person3 );
# 情報を一覧表示する
for my $person (@people) {
    my $name = $person->{name};
    my $mail = $person->{mail};
    print "$name さんのメールアドレスは $mail です\n";
}

リファレンスを使う理由を理解し,応用的に使うことが理解への早道となるでしょう。

変数の中身が意図しない値になる

変数に格納された数値や文字列をコピーしてほかの変数へ渡した場合,それぞれの値への変更は互いに影響を受けません。これを値渡しと呼びます。

それに対してリファレンスをコピーしてほかの変数へ渡した場合,同一の値への参照を共有するため,それぞれの参照先への変更は互いにすべて反映されます。これを参照渡しと呼びます注1⁠。

値渡しと参照渡しの違いは,次のコードの実行結果で確認できます。

reference_data_change.pl
# 値渡し時の挙動
my $number1 = 10;
my $number1_dup = $number1; # コピーを代入
$number1_dup *= 100;
print "もとのデータ: $number1\n"; # => 10
print "渡し先のデータ: $number1_dup\n"; # => 1000

# 参照渡し時の挙動
my $number2 = 10;
my $number2_ref = \$number2; # リファレンスを代入
$$number2_ref *= 100;
print "もとのデータ: $number2\n"; # => 1000
print "渡し先のデータ: $$number2_ref\n"; # => 1000

値渡し時のコードでは,コピーを渡した先の変数$number1_dupに加えた変更は,コピー元の変数$number1の値には影響しません。参照渡しのコードでは,参照している変数$number2_refに加えた変更が,参照されている変数$number2の値にも適用されます。

値渡しと参照渡しの特徴を理解して使い分けましょう。

注1)
Perlのリファレンスを渡す手法は,正しくは参照の値渡しと呼びます。

著者プロフィール

尾形鉄次(おがたてつじ)

大学院卒業後の2003年,Webメール開発会社に入社。以後10年以上,Perlを使ったWeb開発に携わる。

教育に関しては大学でのTAの経験などから課題感を持っていたが,2013年以降にPerl入学式などのコミュニティで教える側に立つことにつながり,そこで得られた知見を社内外で活かしている。

2019年よりJapan Perl Association理事,およびPerl入学式2代目代表を務める。

GitHub:xtetsuji
Twitter:@xtetsuji


和田智(わださとし)

Perl入学式 in YAPC::Asia 2013で受講生としてPerlに触れる。現在は経理業務を中心にバックオフィス業務を行っている。

シーサー㈱
GitHub:sironekotoro


三島智一(みしまともかず)

本職はITとは関連のない仕事に就いているが,2012年にPerl入学式に受講生とした事をきっかけに,趣味でプログラミング,オープンソースコミュニティ活動に参加している。現在はPerl入学式サポーター,Kansai.pm代表を務める。

GitHub:tomcha
Twitter:@tomcha_