アルパカ三銃士

〜アルパカに酔いしれる獣たちへ捧げる〜

知らないであろう @, % シジルのお話

こちらは Perl Advent Calendar 11 日目の記事です。

小ネタです。

社会人になって Perl と触れ合う機会が少なくなりました。Go と PHP を主に書く会社に入社するとこんな感じになります。

しかし、Perl が大好きなので、コードリーディング力を失わないように何か実装するときに必ず一回は CPAN に上がっている Perl のモジュールのロジックを参考にします。また Twitter では Perl 的なコードを読む力を温存するために ThisIsPerl bot をフォローしています。

twitter.com

ある日このコードを読んでいて気づいた事があったのでそれについて記述します。

w;<~+a@/,/v1?@0+4_-&:y[;.)>

このコードは Perl の世界では valid なコードです。 下記を実行して確認することができます。

$ perl -c -e 'w;<~+a@/,/v1?@0+4_-&:y[;.)>'
-e syntax OK

まずはこのコードを簡単に解説します

  1. w; は Perl がクォートが無い列も頑張って文字列として解釈しようとするので valid になります。 "w"; と同じです。
  2. <...> はファイルハンドルになります。 ... がハンドル名です。
    1. と同じように "~+a@/,/v1?" . join($", @0) . "+4_-&:y[;.)" として解釈でき、これがハンドル名になります。

これと似たファイルハンドルの挙動を次のコードで確認できます。

#!/usr/bin/env perl

use feature qw/say/;

my @a=qw!bar!;
open(foobarbaz, "<", "filename.txt");
say <foo@{a}baz>;
say "-"x10;
say <foobarbaz>;
close(foobarbaz);

このコードの結果はこうなります。

$ echo -n hello > filename.txt
$ perl a.pl
foobarbaz
----------
hello

これらは Perl の構文として有効なコードです。

そしてこの中で僕が気になったのは @0 の存在です。

次のコードを実行してみましょう。

#!/usr/bin/env perl

my @0 = 1;

これを実行すると次のようなエラーが返ってくるはずです。

Can't use global @0 in "my" at example.pl line 4, near "my @0 "
Execution of example.pl aborted due to compilation errors.

%1, %2 なども同様です。このエラーは変数がグローバルスコープに存在する変数を my を使ってレキシカルなスコープの変数として扱おうとした場合に発生します。詳しくはこちら

僕は今までこう認識してました。

  • 変数名に数値だけの名前を指定することができない
  • グローバルスコープに存在する変数 == 何か特別な機能を持った変数

どういうことだと perldoc -v "@0" をしましたが見つかりませんでした。そこでインターネットに頼ると的を得た回答が stackoverflow にありました。

Perl identifiers that begin with digits, control characters, or punctuation characters are exempt from the effects of the package declaration and are always forced to be in package main ; they are also exempt from strict 'vars' errors.

stackoverflow.com

どうやら数値、制御文字、句読点で始まる識別子は全て強制的に main パッケージ内で宣言されるものになるらしいです。そしてこれらの @, % シジルに関して特別な機能を持っていない識別子が多いです。

なぜこのような実装なのかは僕の考えとして特別な識別子($_$;, @- など)を my を使って上書きできないようにしたかったのではないかなと考えています。(でないとこの識別子がどんな意味を持つのかスコープ単位で考える必要が出てくるため)

これらの文字を判別するコード(isDIGIT, isCNTRL, isPUNCT) はこちらになります。

github.com

以上から次のコードは全て valid なものになります。

#!/usr/bin/env perl

use Data::Dumper;
use feature qw/say/;

# digit
@0 = 'a'..'c';
%1234 = (a => 1);

say+("-" x 10)." digit";
say '@0: ', Dumper \@0;
say '%1234: ', Dumper \%1234;

# control
@^A = 1..3;
%^[ = (b => 2);

say+("-" x 10)." ctrl";
say '@^A: ', Dumper \@^A;
say '%^[: ', Dumper \%^[;

# punctuation
@; = qw!x y z!;
%; = (c => 3);

say+("-" x 10)." punctuation";
say '@;: ', Dumper \@;;
say '%;: ', Dumper \%;;

明日は MacOlin さんです。楽しみですね!