はじめに
perlの勉強をしていて,調べたことをまとめます.
使用しているperlのバージョンは 5.24.1
です
小ネタ一覧
- サブルーチンへの配列の渡し方
- 変数のスコープ
- 変数への制約
小ネタ
サブルーチンへの配列の渡し方
まず前提として,perlのサブルーチンは複数の引数を渡すと一つの配列を受け取ったと解釈します.
sub f {
my ($x, $y) = @_; # @_が受け取った配列を表す
return $x + $y;
}
print f(1, 2); # 3
配列とスカラー値,配列と配列を同時に渡すと,サブルーチン側では引数を結合した配列を受け取ったことになります.
下の例では3つの実行結果は全て同じです.
sub f {
my $s = scalar(@_);
print "size: $s, ";
foreach my $i (@_) {
print "$i ";
}
}
f(1, 2, 3, 4); # size: 4, 1 2 3 4 (下2つも同様)
f((1, 2, 3), 4); # size: 2, (1 2 3) 4 とはならない
f((1, 2), (3, 4)); # size: 2, (1 2) (3 4) とはならない
サブルーチン側で区切れ目が分からなくなるので,以下のような受け取り方はできません.
sub f {
my (@a, $x) = @_;
print "a: @a, x: $x\n";
}
my @a = (1, 2, 3);
my $x = 4
f(@a, $x); # a: 1 2 3 4, x:
分けて渡したい場合はリファレンスを渡します.
リファレンスはスカラー値のように渡されるので,サブルーチン側では $
で受け取ります.
sub f {
my ($a, $x) = @_;
print "a: $a, x: $x\n";
}
my @a = (1, 2, 3);
my $x = 4
f(\@a, $x);
# a: ARRAY(0x7f7ffc02c798), x: 4
色々試してみました.
f
の結果からサブルーチン側で @a
で受け取ると,@a
は全てが詰まった配列が格納されます.
g
の結果からサブルーチン側で $a
で受け取ると,$a
は渡された引数配列の先頭要素が格納されます.
sub f {
my (@a, $x) = @_;
print "a: @a, x: $x\n";
}
sub g {
my ($a, $x) = @_;
print "a: $a, x: $x\n";
}
my @a = (1, 2, 3);
my $x = 4;
f(@a, $x); # a: 1 2 3 4, x:
f(\@a, $x); # a: ARRAY(0x7f967582c780) 4, x:
g(@a, $x); # a: 1, x: 2
g(\@a, $x); # a: ARRAY(0x7f967582c780), x: 4
ちなみに,サブルーチン側の受け取る順番を逆にするとリファレンスを使わずとも配列とスカラー値を分けて受け取れます.
sub f {
my ($x, @a) = @_;
print "x: $x, a: @a\n";
}
my @a = (1, 2, 3);
my $x = 4;
f($x, @a); # a: 1 2 3, x: 4
ただし,複数の配列を正しく渡すにはこの方法は使えないので,やはりリファレンスを渡した方が確実です.
変数のスコープ
perlでは my
, local
, our
を使って変数のスコープを決定します.
my
my
を使って宣言した変数はレキシカルスコープなります.
レキシカルスコープは,スコープの外と同じ名前の変数を宣言しても,全く別の変数として扱われます.
foo内の $x
は 直上の変数を参照し,bar内の $x
は1行目の変数を参照します.
our $x = 1;
sub foo {
my $x = 2;
print $x; # 2
bar(); # 1
}
sub bar {
print $x;
}
print $x; # 1
bar
の $x
を foo
の $x
で扱うには正しく引数として与える必要があります.
our $x = 1;
sub foo {
my $x = 2;
print $x; # 2
bar($x); # 2
}
sub bar {
my $x = shift;
print $x;
}
print $x; # 1
local
local
を使って宣言した変数はダイナミックスコープとなります.
ダイナミックスコープは,スコープの外と同じ名前の変数を宣言すると,別のメモリに値が確保されますが,
スコープの場所によって参照先を付け替えるような形になります.
my
の場合と異なり,foo内の $x
は local
で宣言された $x
で解決されます.
動作としては my
の2つ目の例と同じになっています.
our $x = 1;
sub foo {
local $x = 2;
print $x; # 2
bar(); # 2
}
sub bar {
print $x;
}
print $x; # 1
our
our
はグローバル変数を定義するのに使います.
パッケージ内の名前空間に変数を紐づけられます.
ちなみに,my
ではパッケージに紐づけられないので,参照できません.
use feature qw[say];
package Foo;
our $our_var = 123;
my $my_var = 567;
package Bar;
say ($our_var // "undef"); # 123
say ($Foo::our_var // "undef"); # 123
say ($Bar::our_var // "undef"); # undef
say ($my_var // "undef"); # 567
say ($Foo::my_var // "undef"); # undef
say ($Bar::my_var // "undef"); # undef
変数への制約
constant
constant
を使うと定数を定義でき,再代入を防ぎます.
(再代入しようとするとコンパイル時にエラーとなる)
use constant x => 10;
print x; # 10
# x = 0; !!! compile error !!!
配列やハッシュはリファレンスの変更を防ぐことはできますが,要素の変更は許容してしまいます.
また,constant
は関数であるためでリファレンスする際,カッコが必要となります.
関数なのでカッコでなく &
で呼び出すことができますが,インライン展開されなくなり,
コンパイル時に値が確定するというメリットがなくなります.
use constant array => [1, 2, 3];
# array = [4, 5, 6]; !!! compile error !!!
array->[0] = 0;
print array->[0]; # 0
print @{array()}; # 023 !!! good !!!
print @{&array}; # 023 !!! not good !!!
インライン展開されていないことは,-MO=Deparse
オプションで確認できます.
use constant array => [1, 2, 3];
print @{array()};
print @{&array};
---
$ perl -MO=Deparse test.pl
use constant ('array', [1, 2, 3]);
print @{[1..3];};
print @{&array;};
sub array () { [1..3] }
test.pl syntax OK
Readonlyモジュール
Readonlyを用いると配列やハッシュに対しても制約をかけられるようになります.
要素の変更もできません.
さらに,my
を用いたレキシカルスコープな定数を作れます.
ただし,constant
に比べて動作が遅く(インライン展開されない),定数を上書きしようとした時実行時エラーとなります.
参考URL
http://www.futomi.com/lecture/local/index.html
[Perl] my と local と our の違いについて
【PERL】MY と OUR と LOCAL と……
Perlにおける定数宣言
おわりに
調べていて詰まったところについてPerlの小ネタをまとめてみました.
間違っている部分があればコメントお願いします.