Perl/変数、データ構造
ここではPerlの変数とデータ構造についての基本的な事項を学びます。応用的な事項についてはリファレンスで触れます。
変数とは[編集]
変数の概念について説明します。変数とは一言で言えばデータを格納する領域です。変数を用いることにより、データをより柔軟に扱うことができます。
変数の利用の形態は大きく分けて代入、参照、展開の3種です。
データ構造の種類[編集]
スカラー[編集]
スカラーとは内部に構造をもたない「ただ一つ」の形をもっているデータ構造です。(後述になるが、「配列」や「リスト」でない、通常の変数なら、スカラーに分類される。詳しくは後述の「配列」の節で説明。)
- コード例
#!/usr/bin/perl use strict; use warnings; my $name = "太郎"; # 変数 $name に「太郎」を代入している print "$name \n";
- 実行結果
太郎
- コード例
#!/usr/bin/perl use strict; use warnings; $age = 30; # 変数 $age に「30」を代入している print "$age \n";
- 実行結果
30
Perlでは、ドルマーク($)を変数名の前につけることで、それがスカラー変数であることを明示する仕組みです。
- ※ 数学のベクトル理論でいう「スカラー」とは意味がやや違い、Perlでは文字列であっても(配列などでなければ、)スカラー変数です。
変数が示している値が数値か文字列かは、値側にある情報「型」により決まります。
上記コードの場合、「name」という名称のスカラー変数 $name と、「age」という名称のスカラー変数 $age があります。
上記コードの結果、
スカラー変数 $name に"太郎"が保存され、
スカラー変数 $age に"30" が保存されます。
このように、一つの変数は、単体のデータを保存できます。
リテラル表記の決まりで、文字列を扱う場合には「"」や「'」で囲みます。数値の場合は「"」や「'」は必要ありません。
- 変数名として使えない文字
ハイフン -
は、変数名に使えません。
また、半数は数値から始める事ができません。
$123age
は変数として不正です。
Perlが実際に各所ごとに自動で判定して数値変数と文字列変数とを分類している様子を、コード実験で確かめてみましょう。
- コード例
$age = 30;
$age = $age + 1; # 足し算をしてみる
print "$age \n";
- 実行結果
31
- コード例
$age = 30;
$age = $age . "1"; # 文字列連結をしてみる
print "$age \n";
- 実行結果
301
- コード例
$age = 30;
$age = $age . "1"; # 文字列連結をしてみる
$age = $age + 1; # 足し算をしてみる
print "$age \n";
- 実行結果
302
- コード例
$age = 30;
$age = $age + 1; # 足し算をしてみる.結果 31 になってる
$age = $age . "1"; # 文字列連結をしてみる. 31に1を連結して311に
print "$age \n";
- (※ Fedora32で2020年6月6日に確認ずみ。)
- 実行結果
311
このように、1つの変数でも、コードによって文字列を指し示したり数値を指し示したり、適宜、使い分けることも出来ます[1]。
C言語との違い[編集]
C言語と違い、Perlでは変数に、(C言語でいう)「整数型」や(Cの)「浮動小数型」などの型(かた)は指定しないです。
つまり、Perlでは、変数を宣言する際、整数や小数との違いは無く、単に
$変数名 = 代入内容;
で、Perl側が自動的にあれこれと判断します。
また、文字列と数値との宣言のちがいすら、Perlの変数宣言には、ないです。
配列、リスト[編集]
概要[編集]
配列、リストは順序付きの複数のデータからなるデータ構造です。ここでいう順序付きとは「配列の最後にデータを付け加える」「前から順番にデータを読み込んで画面に出力する」「1番目のデータを読み込むとともにデータから削除する」といった、順序が存在していることを前提にした処理が可能であるということです。配列とリストは似通っていますが区別する必要があります。
@name = ("太郎", "次郎");
# @name は配列である。「("太郎", "次郎")」 はリスト
または
($name1, $name2) = @name;
# 「($name1, $name2)」はリストである。 @name は配列である。
- コード例
@name = ("太郎", "次郎");
print "$name[1]\n";
- (2020年5月17日に Fedora 32 で動作を確認。)
- 実行結果
次郎
Perlにかぎらず、配列の要素の番号は 0番 から数えるのが一般的です。Perlも同様、配列では 0番 から数えはじめます。
なので上記コードの場合、「太郎」は0番、「次郎」は1番です。
@マークは、配列全体を定義したり、配列全体にアクセスしたりするなどの配列全体をあつかう場合に(@マークを)つかいます。
配列の個々の要素にアクセスする場合には、ドルマーク $ を使います。
たとえば、下記コード
- コード例
@name = ("太郎", "次郎");
print "@name\n";
- (2020年5月17日に Fedora 32 で動作を確認。)
を実行すると
- 結果
太郎 次郎
のように、要素全体が表示されます。
なお、
- (※ エラーコードの例)
@name = ("太郎", "次郎"); print "$name\n"; # この行が間違っている。
としてもエラーになり、表示されないです。
qw演算子というのを使って
@name = qw(太郎 次郎);
print "$name[1]\n";
- (※ Fedora32で2020年6月6日に確認ずみ。)
と書く事もできます。
結果は
次郎
です。 qw演算子は、半角スペースで区切って、順番に要素として配列に代入していきます。
- その他
まず、下記のコードを実行すると、
@test = (1,4,2);
print @test ;
- 実行結果
142
のようになります。
このように、文字をそのまま、つなげます。
区切り文字[編集]
もし「1,4,2」のように区切り文字をつけて出力したい場合、join関数を使い
@test = (1,4,2);
@second = join "," , @test;
print @second ;
のようにします。
join関数の書式は
join "区切り文字", @もとの配列
です。
- 表示結果
1,4,2
sort関数による並び換え[編集]
sort関数を使うと、並べ替えをする事ができます。
数値の場合、大小の順番に並べ替えることができます、。
下記のコードは、小さい数ほど先に、配列の先に移します。
@test = (1,4,2);
@second = sort @test;
print @second ;
- 表示結果
124
これをもし「421」というふうに大きい数から順に並べ替えたい場合は、
@test = (1,4,2);
@second = sort {$b <=> $a} @test ;
print @second ;
とします。 $bとかイキナリ出てきたので混乱しますが、「 sort {$b <=> $a}」をひとまとめで、ひとつの命令だと思ってください。
その証拠に、この「$b」を他の「$c」や「$d」などといった文字列に変えてみても、ソートはされないです。(※ Fedora32 で 2020年6月6日に確認。またWindows版ストロベリーPerlでも同様の結果。)
ともかく、上記コードを実行してみると、
- 表示結果
421
実行してみると、たしかに、大きい数から順に実行している事が分かります。 なお、「 sort {$b <=> $a}」の演算子 <=> は、Perlの仕様では宇宙船演算子と言われるものです。
形が昔のアメリカのSFドラマに出てくる宇宙船に似ている事からの命名です(由来は諸説あるので、本wikiでは立ち入らない)。
なお別プログラム言語のPHPにも宇宙船演算子はあります(PHP7から)。
区切り文字をつけたいなら、join関数と組み合わせて、たとえば下記コードのように、
- コード
@test = (1,4,2);
@test = sort {$b <=> $a} @test ;
@test = join "," , @test;
print @test ;
- 表示結果
4,2,1
のように書けます。
区切りのカンマがいらずに単に半角スペースで区切りたいなら、下記のように単にprint関数の二重引用符で出力するだけで、自動で半角スペースも入ります。
- コード
@test = (1,4,2);
@second = sort {$b <=> $a} @test ;
print "@second\n" ;
- 表示結果
4 2 1
$a と $b (宇宙船)[編集]
宇宙船演算子で使われるとされている変数 $a や $b に他の数値や文字列を入れると、どうなるのだろうか?
実際にLinux(Fedora32) で試してみると、aやbに何をいれようが、sortの結果は同じで、通常どおりにソートが実行される。
- コード
@test = (1,4,2);
$a = -1234 ;
@second = sort {$b <=> $a} @test ;
print @second ;
- 表示結果
421
$a = "qwer" ; とかデタラメな文字列に変えてみても、表示の結果は同様「421」である。
$a でなく $bだけ何かを代入してみても同様、表示の結果は「421」である。
結局、LinuxのPerlでは、命令「 sort {$b <=> $a} 」で ひとまとめの組み込み関数のように振舞っている。
なので、原理的には、$aや$Bを、一般の数値計算などの他の用途に使い回すことも可能。(ただし、まぎらわしいので、実務では、下記のような一つのコードでの混用は避けるべき。)
- コード例
@test = (1,4,2);
# 数値計算
$a = 100 ;
$b = 5 ;
$w = $a/$b;
print "$w \n";
# 並び換え
@second = sort {$b <=> $a} @test ;
print "@second \n";
- 表示結果
20 4 2 1
Perl利用者にとっても、「$a」や「$b」のような簡易な命名は、よく入門的なプログラムでも使うだろうから、利用者にとっても「 sort {$b <=> $a} 」で ひとまとめの組み込み関数として処理したほうが、ユーザーの定義した変数 と 組み込み関数 とが干渉しないので、便利である。(というか、もし万が一、$aや$bを書き換えることによってソートに干渉してソートの結果が変わってしまう仕様だと(現在はその仕様ではないので安心してよい)、初心者プログラマーは変数の宣言の際に、いちいちこの変数を避ける手間が生じてしまうので不便になりかねない。)
ハッシュ[編集]
ハッシュとは、キーと値がペアになったデータ構造です。また、ハッシュは配列とは違って、順番は特に指定されては管理されていないです。
書式は
%ハッシュ名 = (
"キー名1" => 値A,
"キー名2" => 値A,
# 以降は省略
);
の書式です。
- コード例
%age = (
"太郎" => 30,
"二郎" => 20,
);
print $age{"太郎"} . "\n" ; # 「30」と表示
- (2020年5月17日に Fedora 32 で動作を確認。)
このように、キーの文字列でアクセスすると対応する値が得られます。キーは必ず文字列として扱われます。
=> 演算子はコンマ演算子と同じ働きをしますが、左オペランドの値を必ず文字列として扱うため、ハッシュを生成するときに多く用いられます。また、コンマを使うよりもキーと値の対応が明確になるという利点もあります。
ハッシュはキーと値が関連付けられたリストです。配列はデータの並び順が決まっていますが、ハッシュはランダムに格納されるため、データの順番が保証されません。ただ、キーと値がペアになっているということのみが保証されます。
%age = (
"太郎" => 30,
"二郎" => 20,
);
print %age; # 太郎30二郎20 あるいは 二郎20太郎30
- (2020年5月17日に Fedora 32 で動作を確認。)
- 他言語の関連事項
もし読者がJavaScriptの文法を知っているなら、JavaScriptのデータ形式のJSONに、記法の若干のちがいがあるものの、用語が近いです。
また、Pythonの辞書型にも、内容が近いです。
Perlが扱うデータ[編集]
他言語と比べ、Perlでは文字列と数値の区別は曖昧で、良く言えば柔軟で直感的である。
下記の実行結果は52になる。(文字列は数値として解釈されるときは0として扱われる。)
- コード例
$a = 52; $b = "nd street"; print $a + $b;
- (2020年5月17日に Fedora 32 で動作を確認。)
- 実行結果
52
いっぽう、下記コードの実行結果は"52nd street"になる。
- コード例
$a = 52; $b = "nd street"; print $a . $b;
- (2020年5月17日に Fedora 32 で動作を確認。)
- 実行結果
"52nd street"
文字列[編集]
「Hello World」 のような文字列をPerl で扱う場合、二重引用符(ダブルクォーテーション) " 内部の文字列 " で囲みます。
つまり
"Hello World"
のように使います。
シングルクォーテーション ' 文字列 ' で囲んでも文字列として宣言できますが、エスケープシーケンス( \n など)がシングルクォーテーションでは動作しないです。
また、シングルクォーテーションでは変数も使えません。
ダブルクォーテーションなら、変数も使えます。(JavaScriptなどの他の言語でいうところの「テンプレート」構文のような機能が、PHPのダブルクォーテオーションには、あります。)
文字列どうしを連結するには ドット記号( . ) を使います。
- コード例
$myouji = "ヤマダ";
$sitanona = "タロウ";
print ( $myouji . $sitanona . "\n" ) ;
- (2020年5月17日に Fedora 32 で動作を確認。)
- 実行結果
ヤマダタロウ
いっぽうPerlでは、+演算子を使うと、なるべく数値の加算として解釈しようとするので、文字連結では意図しない結果になる場合もあります。
なので、文字連結したいなら、なるべくドット記号を使うのがオススメです。
- 備考
Perlの文字列の型は、長さに制限のない文字列です。 実際にはどんなビット列でも格納でき、(Cなどの言語と違って)NULL (\0) を文字列中に含めることさえできます。
Perlには1文字だけを格納する専用の文字型は存在しません(つまり、C言語でいう char型に相当するものは Perlには無い)。
よって Perl の場合、一文字の内容の文字は単に、何文字でも格納できる型に、たまたま文字を1つだけ格納している状態の文字列であるとして認識されます。
数値[編集]
環境に依存する実数です。 10進数, 2進数(先頭に0bを付加する), 8進数(0を付加する), 16進数(0xを付加する)によって数値を表現できます。 また、科学的記数法による表現も可能です:
print 5e3; # 5000
- (2020年5月17日の現在、Linux の Fedora 32 では上記コードで「5000」を表示できることを確認ずみ。)
表現できる数値の範囲は環境に依存しますが、無限長の実数を表現できるMath::BigFloatなどのモジュールが用意されています。
リファレンス[編集]
後の章で詳しく説明しますが、他のデータを参照するデータです。 複雑なデータ構造を構築したり、Perlのオブジェクト指向機能を使う際に重要なデータ型です。
特殊変数[編集]
プログラマーが変数を宣言しなくても、いくつかの変数は機能が決まっていて、事前にPerlに用意されており、Perlのそういう変数を特殊変数と言います。
プログラム名[編集]
たとえば特殊変数 $0 は、プログラム名が代入されています。
- コード例
print $0 ;
- (2020年6月6日に Fedora 32 で動作を確認。)
- 実行結果
プログラム名が表示される。たとえばプログラム名が「hello.pl」なら
hello.pl
のようにコマンドラインなどに表示される。
他の値を代入すると、もとの値は消えてしまいます。
- コード例
$0 = "gggg";
print $0 ;
- 実行結果
gggg
- コード例
print $0 ;
$0 = "gggg";
- 実行結果
- ※ プログラムのファイル名が表示される。
OS名[編集]
特殊変数 $^O にはOS名が入っています。
print $^O;
- (2020年6月6日に Fedora 32 で動作を確認。)
- 実行結果
linux
実行結果は、使用しているOSによって変わります。
プロセスID[編集]
特殊変数 $$ には、そのプログラム使用時のプロセスIDが入っています。
- コード例
print $$;
- (2020年6月6日に Fedora 32 で動作を確認。)
- 実行結果の1回目の例
7227
- 実行結果の2回目の例
7297
プロセスIDはプログラムを実行するたびに、いくつかの値が足されていくので、上記のように実行するたびに値が変わります。表示される数値は使用状況によって異なりますので、上記の例は一例です。
コンテキスト[編集]
変数、関数、定数などが、式の中でどのように評価されるか決定するものです。
大別するとスカラ・コンテキストとリスト・コンテキストがあり、スカラ・コンテキストにおかれた値はスカラとして、リスト・コンテキストにおかれた値はリストとして評価されます。
コンテキストと実際のデータが食い違っている場合、次のような規則で評価されます。
- スカラ・コンテキストにリストがおかれた場合、リストの最後の要素が評価されます(コンマ演算子の為)。
- リスト・コンテキストにスカラがおかれた場合、そのスカラ1個だけを要素とするリストであると解釈されます。
どのようにコンテキストが提供されるか、以下にいくつか例を示します。
代入式は右辺に、左辺と同じコンテキストを提供します:
my @array = qw(Foo Bar Baz); my $var = @array; #3が代入される
qw は、つづく丸カッコ内をスペースで区切ってリスト化する演算子です。詳しくは『Perl/演算子#クオート演算子』を参照してください。
配列はスカラ・コンテキストで評価されるとその要素数を返すので、結果として$numberには3が代入されます。
ただしこのような結果になるのは配列だけです。
前述したとおり、通常のリストがスカラ・コンテキストで評価されると、最後の要素が返されます:
my $var = qw(Foo Bar Baz); #'Baz'が代入される
my ($foo, $bar, $baz) = 'Foo'; # リスト・コンテキスト
右辺はスカラですが、左辺がリスト値を期待している為、1つの要素'Foo'のみを持つリストと解釈されます。
これが $foo に代入されますが、残りの2つの変数については、対応する右辺値がない為未定義となります。
したがって、これは次のコードと等価です:
my ($foo, $bar, $baz) = ('Foo', undef, undef);
フォワード宣言されたサブルーチンは、デフォルトで引数にリスト・コンテキストを提供します:
sub user_func; user_func 'foo', 'bar', 'baz';
これは次のように解釈されます。
user_func('foo', 'bar', 'baz');
つまり、括弧のないサブルーチン呼び出しはリスト演算子として扱われます。
もしこれを、
user_func('foo'), 'bar', 'baz'
と解釈させたいのなら、フォワード宣言にプロトタイプを付加することによって単項演算子として解釈させることができます:
sub user_func($); # 実装にもプロトタイプが必要 user_func 'foo', 'bar', 'baz';
サブルーチンとフォワード宣言、プロトタイプについての詳細はサブルーチンを参照してください。
スカラ・コンテキストはさらに文字列、数値、真偽値、無効コンテキストに細分され、評価されます。
文字列コンテキスト[編集]
長さに制限のない文字列として扱われます。
数値はそのまま文字列に変換され、未定義値は空文字列になります。リファレンスも文字列になりますが、文字列として処理されたリファレンスを再びリファレンスに戻すことはできません:
my ($var, $refvar, $refstr); $var = 'foo'; $refvar = \$var; #$$refvar eq 'foo' $refstr = "$refvar"; # 文字列として格納 $$refstr; #エラー; $refstrはもはやリファレンスではない
数値コンテキスト[編集]
10進数の表記に使われる文字列は数値として扱われます。それ以外の文字があるとそこで解釈が終了します。
#全て12345と解釈される。 my $numstr = '12345'; my $numstr2 = '12345abcde'; my $numstr3 = '123.45e2';
先頭に'0'や'0x'があっても8進数や16進数とは解釈されないので注意が必要です。この場合 oct() 関数や hex() 関数を利用します。
my $numstr4 = '012345'; # 12345; 8進数ではない my $numstr5 = '0x12345'; # 0; 'x'で解釈が終了する
組み込みの算術演算子は、オペランドに対して数値コンテキストを提供します。 よって、値に0を足すことで強制的に数値として解釈させることができます:
print '12345abcde', "\n"; # 12345abcde print '12345abcde' + 0, "\n"; # 12345
真偽値コンテキスト[編集]
ifやwhileなどの制御構文や修飾文、andやorなどの論理演算子が提供するコンテキストです。
偽となるものは:
- 数値
0
- 文字列
'0'
- 空文字列
''
- 未定義値
であり、残りは全て真と解釈されます。
「文字列'0'」とは'0'という文字列のことであり、数値コンテキストで0と解釈される文字列全てのことではないので注意してください。
次のものは全て真となります:
'0.0'; 'aaa'; '0 but true';
無効コンテキスト[編集]
評価した結果が捨てられてしまうので、値を期待しないコンテキストです。戻り値のない関数呼び出しなど、副作用を目的として使われます。
副作用もないコードは、perlに-wスイッチをつけて実行すると警告が発せられます:
'literal';
型グロブ[編集]
Perlでは異なるデータ構造に対して同じ識別子を与えることができます:
$foo = 'bar'; @foo = ( 'bar', 'baz' ); %foo = ( bar => 'baz' ); sub foo { return 'bar' };
Perl処理系は内部に識別子テーブルと呼ばれるハッシュを持っています。そのキーは識別子であり、対応する値は型グロブというデータ構造です。型グロブは同じ識別子を持つすべてのデータ構造へのリファレンスを格納しています。つまり上記の例だと識別子'foo'の型グロブにはスカラー、配列、ハッシュ、サブルーチンという4つのデータ構造へのリファレンスが格納されています。型グロブは識別子の前に'*'というプレフィックスを付加して表現されます:
*foo;
型グロブ自身はリファレンスを格納したハッシュであり、キーはデータ構造の名前です:
*foo{SCALAR}; # \$foo *foo{ARRAY}; # \@foo *foo{HASH]; # \%foo *foo{CODE}; # \&foo *foo{GLOB}; # \*foo; 自分自身へのリファレンス *foo{IO}; # ファイルハンドル *foo{FORMAT} # フォーマット
型グロブへの代入[編集]
型グロブもデータ構造の一つですから、代入や評価ができます。型グロブに別の型グロブを代入すると、変数の別名(エイリアス)を定義することが出来ます:
$foo = 'FOO'; @foo = ( 'FOO', 'BAR' ); *bar = *foo; $bar = 'BAR'; push( @bar, 'BAZ' ); print $foo, "\n"; #BAR print @foo, "\n"; #FOOBARBAZ
これはかつてPerlにリファレンスがなかった頃、サブルーチンに引数を参照渡しするのに利用されていました。また、ファイルハンドルとフォーマットにはプレフィックスが存在しないので、これらを受け渡しする場合の唯一の手段でもありました。
for ( $i = 0; getline( *line ) != -1; $i++ ) { print "line $i: $line"; } sub getline { local (*l) = @_; return defined( $l = <STDIN> ) ? length( $l ) : -1; }
現在ではリファレンスが利用できるので、型グロブを使う必要はありません。ファイルハンドルやフォーマットに関してもIOモジュールなどでオブジェクトとして扱うことができます。
なお、型グロブは識別子テーブルの実体そのものですから、ブロックに結び付けられたレキシカルスコープにすることはできません。言い換えると、local変数にはできるがmy変数にはできません。
また、特定のデータ型のリファレンスを代入すると、そのデータ型に限定して別名を定義できます:
$foo = 'FOO'; @foo = ( 'FOO', 'BAR' ); *bar = \@foo; #配列のみ別名を定義 $bar = 'BAR'; push( @bar, 'BAZ' ); print $foo, "\n"; #FOO print @foo, "\n"; #FOOBARBAZ
*qux = \&Foo::Bar::baz; # Foo::Barモジュールのbaz関数をqux関数としてインポートする
- ^ 同じ変数が、同じプログラム中で違う型のオブジェクトを差示すのは、特に理由がない限り混乱のもとになるので避けるべきです。