#Perl入学式 第3回
- 会場について
- 写真撮影について
- 講師・サポーター紹介
- 自己紹介
##今日の流れ
- 前回の復習
- 配列 (関数)
- ハッシュ
- リファレンス
$, @, %, & といった、変数名の前に付く記号の事を指します。
('値をコンマで区切って', '並べて', 'カッコで囲んだもの')
- 変数
- 標準入力
- 演算子
- if, else 文
- for 文
- 配列
practice.md
の sum.pl
と even_or_odd.pl
をやってみましょう。
前回、皆さんは配列の基本を勉強しました。
さて、そもそも配列ってどんな時に使う (使いたくなる) と思いますか?
- 要素間の順序関係を表現したい場合 (e.g. 待ち行列)
- 要素の単純な集まりとして表現したい場合 (e.g. 集合)
そしてそれらを同じように操作したい時!
配列の要素を自由に
- 追加
- 取り出し
できる必要があります。
- push
- unshift
- pop
- shift
- push / pop
- unshift / shift
my @array = ('Alice', 'Bob');
push @array, 'Chris'; # ('Alice', 'Bob', 'Chris')
my $element = pop @array; # ('Alice', 'Bob')
print $element; # => "Chris"
配列が
□□□
こういう状態の場合...
my @array = ('Alice', 'Bob');
unshift @array, 'Zappa'; # ('Zappa', 'Alice', 'Bob')
my $element = shift @array; # ('Alice', 'Bob')
print $element; # => "Zappa"
配列が
□□□
こういう状態の場合...
- 次のような操作をするコードが書かれた
array_func1.pl
を作成しましょう.- ('Alice', 'Bob', 'Chris') という配列を作って出力してみましょう。
- 関数を使って 'Alice' を取り出して出力してみましょう。
- 関数を使って 'Chris' を取り出して出力してみましょう。
- 次のような操作をするコードが書かれた
array_func2.pl
を作成しましょう.- ('Alice', 'Bob', 'Chris') という配列を作って出力してみましょう。
- 1.の配列を元に、関数を使って ('Zappa', 'Alice', 'Bob', 'Chris', 'Diana') という配列を作って出力してみましょう。
my @array = qw( Alice Bob Chris ); # ('Alice', 'Bob', 'Chris')
-
これまでのリストの書き方,
('値をコンマで区切って', '並べて', 'カッコで囲んだもの')
と違って、クォート記号が不要です。 -
空白文字 (スペース、タブ、改行など) は捨てられ、残ったものがリストの要素になります。
-
なので、こういう書き方もできます。
my @arary = qw( Alice Bob );
# デリミタ (区切り文字) には任意の記号文字が使えます。例えば...
qw! Alice Bob !
qw# Alice Bob #
qw/ Alice Bob /
これらは前述の
qw( Alice Bob )
と等しくなります。
qw/http://www.perl-entrance.org http://www.yahoo.com/
# これは困った! エラーになってしまう!
# デリミタがスラッシュ (/) で、要素中にもスラッシュが含まれてしまっている
解決法は2つ
# 1. スラッシュをバックスラッシュでエスケープしてあげる
qw/ http:\/\/www.perl-entrance.org http:\/\/www.yahoo.com / # => 読みにくく感じるかもしれない
# 2. デリミタを変える
qw! http://www.perl-entrance.org http://www.yahoo.com ! # => ちょっと読みやすいかもしれない
split は指定したパターンに従って文字列を分割します。
my $poem = 'I Love Perl.';
my @words = split / /, $poem; # => ('I', 'Love', 'Perl.')
引数として与えた文字列を // でくくった文字 (上の例だと半角スペース) で分割して配列に格納します。
(ところで、// は正規表現リテラルと呼ばれるものです。正規表現リテラルの話は長くなるので、次回以降に紹介します。今は「そういうものなんだ…」と耐えてください)
join は 分割された文字列をくっつけて1つの文字列にします。(split の逆の働きですね)
my @words = qw( I Love Perl. ); # => qw ショートカット
my $poem = join '_', @words;
print $poem; # => 'I_Love_Perl.'
join の受け取る第1引数は糊のようなものです。リストの要素をくっつけるときに、その間にはさみます。 (今回の場合だと、 '_' という風に指定しているので、リストの各要素をアンダースコアでくっつけます) 第2引数はバラバラになった文字列の配列です。
reverse は リストを逆順に並べ替えてリストで返します。
my @lang = qw(perl php ruby python java go);
my @reversed = reverse @lang;
print "@reversed"; # => go java python ruby php perl
sort は リストをルール順に並べ替えてリストで返します。
my @lang = qw(perl php ruby python java go);
my @sorted = sort @lang;
print "@sorted"; # => go java perl php python ruby
今回の例ではルールを指定しなかったので文字コード順に並び替えられました。 ルールを指定すればさらに複雑な並び替えも可能です。 => http://perldoc.jp/func/sort
- 次のような挙動をするコードを,
join.pl
として作成しましょう.- ("0120" "123" "XXX") という内容の配列を qw ショートカットを使って作ってみましょう。
- 上の問題で作った配列を繋げてみましょう。なんだか電話番号っぽいので、ハイフン ('-') を間にはさみましょう。
- 次のような挙動をするコードを,
split.pl
として作成しましょう.- 文字列 "/usr/bin/env perl" という文字列をスラッシュ ('/') で分割し、配列に格納してみましょう。
- その配列を出力した時の結果を予想してみましょう. 予想出来たら実際に出力してみましょう. 期待通りに出力されましたか? そうでない場合, なぜそのような挙動になったか考えてみましょう(わからなければ, サポーターの方に聞いてみましょう!)
- Perl のデータ構造のひとつで、「連想配列」とも呼ばれます。
- 配列と同じく、任意個の値の格納・取り出しができます。
- ハッシュは配列とは異なり、「数値」ではなく「名前」を使って、格納した個々の「値」を指定します。
- 要素を指定する為の名前を "key"、key によって指定された要素を "value" と呼びます。
- 名前: Alice
- 職業: Programmer
- 所属: PerlEntrance
...という人をデータとして表現してみましょう。
配列の場合だとこんな感じになるのではないでしょうか。
my @user = ('Alice', 'Programmer', 'PerlEntrance');
print $user[0]; # 最初の要素が表示される => "Alice"
print $user[1]; # 2番目の要素が表示される => "Programmer"
print $user[2]; # 3番目の要素が表示される => "PerlEntrance"
@user
の添字0
が名前, 1
が職業... といった形で覚えなくてはなりません. 面倒ですね.
my %user = (
name => 'Alice',
job => 'Programmer',
affiliation => 'PerlEntrance'
);
print $user{name}; # 名前 (name) が表示される => "Alice"
print $user{job}; # 職業 (job) が表示される => "Programmer"
print $user{affiliation}; # 所属 (affiliation) が表示される => "PerlEntrance"
なんだかわかりやすい感じがしますね!
- ハッシュは % を使って定義します。
- ハッシュにアクセスするときは、添字として波括弧 {} を使います。
- 1つのハッシュ内に、文字列や数値が混在していても構いません。
百聞は一見に如かず。以下の例を見てみましょう。
my %hash; # ハッシュを定義
$hash{name} = 'Alice'; # "name" という key に "Alice" という value を代入
$hash{age} = 16; # "age" という key に "16" という value を代入
print "$hash{name}\n"; # => "Alice"
print "$hash{age}\n"; # => 16
ハッシュは配列の一種なので、こういう書き方もできます。
my %hash = ('name', 'Alice', 'age', 16);
print "$hash{name}\n"; # => "Alice"
print "$hash{age}\n"; # => "16"
実際には、こういう風に書くのが便利で良い感じです。
my %hash = (
name => 'Alice', # ファットコンマ演算子 (=>) で key と value の関係を書ける
age => 16 # 左側の値は文字列として解釈されるのでクォートの必要がない
);
print "$hash{name}\n"; # => "Alice"
print "$hash{age}\n"; # => "16"
- ファットコンマ演算子は基本的にコンマのようなものです。
- ハッシュではファットコンマ演算子を使うと、 key と value の関係性(
=>
の左がkey, 右がvalue)が見やすくなるので便利です。
my @user = ('Name', 'Job', 'Affiliation');
これがある日突然
my @user = ('Affiliation', 'Job', 'Name');
という風に順序が入れ替わったらどうしましょう? 今まで動いていたコードを書き換えなくてはならなくなります。
ハッシュだと、順序ではなく key で対応付けられているので、そういった問題が起こりません。
- 次のような挙動をするプログラムを
hash_profile.pl
という名前で作成しましょう.
- 自分の名前 (name)、性別 (sex)、好きな食べ物 (food) を key にしたハッシュを作ってみましょう。
- key である
name
,sex
,food
を用いて、それぞれの value をprintしてみましょう。
- keys
- values
- delete
- exists
my %hash = (
name => 'Alice',
job => 'Programmer',
affiliation => 'PerlEntrance'
);
my @keys = keys %hash;
print "@keys\n"; # => "name job affiliation"
keys 関数はそのハッシュの key を配列にして返してくれます。
この時、key は順不同になります。
my %hash = (
name => 'Alice',
job => 'Programmer',
affiliation => 'PerlEntrance'
);
my @values = values %hash;
print "@values\n"; # => "Alice Programmer PerlEntrance"
values 関数はそのハッシュの value を配列にして返してくれます。
この時、value は順不同になります(常に同じ順番で帰ってくるとは限りません)。
もし, 常に同じ順番にしたいのであれば, sort
を使って並び替えましょう
my %hash = (
name => 'Alice',
job => 'Programmer',
affiliation => 'PerlEntrance'
);
delete $hash{affiliation};
# この時、%hash は以下のようになっています
# %hash = ( name => 'Alice', job => 'Programmer' );
delete 関数は指定したハッシュの key と、それに対応する value を削除します。
my %hash = (
name => 'Alice',
job => 'Programmer',
affiliation => 'PerlEntrance'
);
if (exists $hash{job}) { print "exists" } # => exists
if (exists $hash{team}) { print "exists" } # =>
exists 関数は指定したハッシュの key が存在するかを確認します。
- 次のような処理を実行する
hash_func.pl
を作成しましょう.hash_profile.pl
で作ったハッシュを用意し, そのハッシュの key の一覧を出力してみましょう。- 3.で, 性別の要素を削除します. その前に,
exists
で性別の要素が存在するかどうかを確認してみましょう。 - ハッシュから、性別の要素を取り除いてみましょう。
- 性別の要素を削除した後, きちんと削除したか確認します. key の一覧を表示した後,
exists
で性別の要素が存在しないことを調べ, きちんと削除されている場合は「性別の要素は存在しません」と表示するようにしてみましょう.
リファレンスとは参照(実体を指し示すもの)です。
リファレンスを使うと複雑なデータ構造を表現できます。
例えば、リファレンスを使うとハッシュの中にハッシュを格納し、更にそのハッシュにハッシュを格納する……といった複雑なデータ構造を表現できます。
| 0 | 1 |
-----+----------+----------+
0 | nqounet | boolfool |
-----+----------+----------+
1 | macopy | papix |
-----+----------+----------+
テーブルのようなデータ構造を表現したいとき
my @osaka = ('nqounet', 'boolfool');
my @tokyo = ('macopy', 'papix');
my @perl_entrance = (@osaka, @tokyo);
print $perl_entrance[1][1]; # => これはエラーになる
今までの知識であれば、配列を用いて行単位の表現はできますが、n 行 m 列目は papix
という様に表現したいですね。
my $osaka = ['nqounet', 'boolfool'];
my $tokyo = ['macopy', 'papix'];
my @perl_entrance = ($osaka, $tokyo);
print $perl_entrance[1][1]; # => papix
リファレンス を用いることで、このように複雑なデータ構造を表現することが容易になります。
関数の引数に複数の配列を渡す必要がある場合などに使います。
また、関数に引数として配列やハッシュを渡すとそれらをコピー (クローン) してしまうので、非効率になる場合があります。
リファレンスはコピーするのではなく参照を渡すだけなので、その問題を回避することができます。
(これらはまた後で出てきます)
my $scalar = 'scalar';
my $scalar_ref = \$scalar # スカラーのリファレンス;
my @array = ( 'this', 'is', 'array' );
my $array_ref = \@array; # 配列のリファレンス
my %hash = ( this_is => 'hash' );
my $hash_ref = \%hash; # ハッシュのリファレンス
それぞれのシジルの前にバックスラッシュ (\) を置くことによってそれぞれリファレンスになります。
my $array_ref = ['This', 'is', 'array-ref'];
普通のかっこ () ではなく角かっこ [] によって要素をくくることで、配列のリファレンスになります。
my $hash_ref = {
this_is => 'hash_ref'
};
普通のかっこ () ではなく波括弧 {} によってくくってやることで、ハッシュのリファレンスになります。
my $array_ref = ['This', 'is', 'array-ref'];
print $array_ref;
ARRAY(0x7fd8938060b8)
みたいな出力が得られると思います (括弧の中身は実行環境によって異なります)
スカラーリファレンスの場合はSCALAR()、ハッシュリファレンスの場合はHASH() とそれぞれ表示されます。
最初に言ったように、リファレンスは参照です。
SCALAR() や ARRAY()、HASH() の括弧の中身に書いてあるのはその実体が格納されているアドレス(場所の情報)です。
そこで出てくるのが“デリファレンス”です。
リファレンス(参照)から、元のスカラーや配列やハッシュ(実体)を得るための操作です。
my $scalar = 'scalar';
my $scalar_ref = \$scalar;
print $$scalar_ref; # アドレスではなくスカラーが表示される => "scalar"
my $array_ref = ["I'm", "scalar-ref"];
print @$array_ref; # アドレスではなく配列の中身が展開される => "I'mscalar-ref"
my $hash_ref = { this_is => 'hash-ref' };
print %$hash_ref; # アドレスではなくハッシュの中身が展開される => "this_ishash-ref"
my $scalar = 'scalar';
my $scalar_ref = \$scalar;
print $$scalar_ref;
スカラーをデリファレンスするためには、そのリファレンスの前にスカラーのシジル ($) を置きます。
my $array_ref = ['zero', 'one', 'two'];
print @$array_ref; # => 配列の中身が展開されます (zeroonetwo)
print ${$array_ref}[1]; # => 'one'
print $array_ref->[1]; # => 上と同じ。こう書く方がベター
配列をデリファレンスするためには、そのリファレンスの前に配列のシジル (@) を置きます。
配列の要素に対してデリファレンスしたい場合 (配列リファレンスから要素を取り出したい場合)、
配列リファレンスをスカラーとしてデリファレンスして波括弧でくくり、通常の配列のようにアクセスすると内容を得られます。
また、中括弧と$を取り除き、その代わりにアロー (->) を入れることによりアクセスする事もできます。
my $hash_ref = { key => 'value' };
print %$hash_ref; # => ハッシュの中身が展開されます (keyvalue)
print ${$hash_ref}{key}; # => 'value'
print $hash_ref->{key}; # => 上と同じ。こう書く方がベター
ハッシュをデリファレンスするためには、そのリファレンスの前にハッシュのシジル (%) を置きます。
ハッシュの要素に対してデリファレンスしたい場合、ハッシュリファレンスをスカラーとして
デリファレンスして波括弧でくくり、通常のハッシュのようにアクセスすると内容を得られます。
また配列同様、波括弧と$を取り除きその代わりにアロー (->) を入れることによりアクセスする事もできます。
中身を確認するために毎回デリファレンスをするのは面倒です。
このようなときに Data::Dumper
モジュールを用いると、一度に中身を見ることができます。
use Data::Dumper;
my $hash_ref = {
name => 'Kurt',
job => 'Guitarist',
affiliation => 'NIRVANA'
};
print $hash_ref; # => HASH(0x7f88d08060b8)
print Dumper($hash_ref);
# $VAR1 = {
# 'name' => 'Kurt',
# 'job' => 'Guitarist',
# 'affiliation' => 'NIRVANA'
# };
my $papix = {
name => 'papix',
address => 'Tokyo',
age => 25,
};
上記のような名前・住所・年齢といった要素を持つリファレンスを新たに作成してみましょう。
さらに Data::Dumper
を用いて、作ったリファレンスを出力してみましょう。
コードは, reference_dump.pl
という名前で保存してください.
my $scalar = 'scalar';
my $scalar_ref = \$scalar;
print $scalar; # => 'scalar'
$$scalar_ref = 'changed!';
print $scalar; # => 'changed!'
リファレンスはコピーではなく参照なので、デリファレンスして更にその中身を変更すると、参照元(実体)が破壊されます。
score.pl には上記のようなハッシュリファレンスがいくつか宣言してあります。
各人物の perl
, ruby
, python
... といった言語の合計値を key sum
の value としてリファレンスに追加しましょう。
例: $papix
の合計値は 270
なので、以下のようになります。(ただし、key の順番がこの通りになるとは限りません)
my $papix = {
name => 'papix',
affiliation => 'namba.pm',
perl => 60,
python => 50,
ruby => 50,
php => 80,
binary => 30,
sum => 270, # => 各言語の合計ポイントが格納されている!
};
practice.md には練習問題の続きがあるので、チャレンジしてみてください。
-
https://github.com/perl-entrance-org/workshop-2014-03/blob/master/practice.md
- 今回の内容を復習することができる問題集です。
- 不明点があれば、気軽にサポーターに質問してください。