Opus 21の続き。今日は配列について研究します。

Open House London - Lloyd's building 1

 まず、Perl 5で配列を検索するプログラムを作成してみます。

#! /usr/local/bin/perl
# arrP5.pl -- Perl 5で配列操作

use strict;
use warnings;
use 5.10.0;

my $num = shift;

my @month = qw (DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
say "The month No. $num is $month[$num]";

 3パターン実行します。

[sample]$ ./arrP5.pl 1
The month No. 1 is Jan
[sample]$ ./arrP5.pl 3
The month No. 3 is Mar
[sample]$ ./arrP5.pl 12
The month No. 12 is Dec

 問題無いですね。
 @monthという配列に、qw演算子を使って13個の文字列を入れます。
 配列はゼロ始まりなので、ゼロ月にあたる最初の配列要素には"DUMMY"という文字列を入れています。

 これを引数から取得した$numを指標として検索するには$month[$num]とします。
 配列要素はスカラーだからシジルは@でなくて$だよ、と口を酸っぱくして言って来ましたね。
 さて、これをPerl 6に移植してみます。

#! /usr/bin/env perl6
# arrP6.p6 -- Perl 6で配列操作

my $num = @*ARGS.shift;

my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
"The month No. $num is @month[$num]".say;

 まず、引数なしのshift関数がプログラムの引数を取ってくるということがなくなりました。もし上のプログラムを

#! /usr/bin/env perl6
# arrP6_bug1.p6 -- Perl 6で配列操作(間違い1)

my $num = shift;

my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
"The month No. $num is @month[$num]".say;

と書くと、

[sample]$ ./arrP6_bug1.p6 12
===SORRY!=== Error while compiling ./arrP6_bug1.p6
Calling shift() will never work with proto signature (@)
at ./arrP6_bug1.p6:4
------> my $num = ⏏shift;
[sample]$

と怒られます。では、

#! /usr/bin/env perl6
# arrP6_bug2.p6 -- Perl 6で配列操作(間違い2)

my $num = @ARGV.shift;

my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
"The month No. $num is @month[$num]".say;

と書くとどうなるかというと、

[sample]$ ./arrP6_bug2.p6 12
===SORRY!=== Error while compiling /Users/query1000/Dropbox/kpc/sample/./arrP6_bug2.p6
Variable '@ARGV' is not declared
at /Users/query1000/Dropbox/kpc/sample/./arrP6_bug2.p6:4
------> my $num = ⏏@ARGV.shift;
[sample]$

とやはり怒られます。@ARGVという配列はなくなり、ここは

my $num = @*ARGS.shift;

と書くことになりました。

[sample]$ ./arrP6.p6 0
The month No. 0 is DUMMY
[sample]$ ./arrP6.p6 2
The month No. 2 is Feb
[sample]$ ./arrP6.p6 11
The month No. 11 is Nov
[sample]$

 さて、サブルーチンにはシグネチャというものが導入されたので、プログラム全体をsub MAIN (引数) { ... } というブロックで囲むこともできます。

#! /usr/bin/env perl6
# arrP6_2.p6 -- Perl 6で配列操作2

sub MAIN (Int $num) {
 my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
 "The month No. $num is @month[$num]".say;
}

 見た目が大幅に変わりましたが、メインプログラムはMAINという名前のサブルーチンで(MAINなのにsub)、$numという1個のInt型の引数を取ります。
 これで、shift関数を使わずに引数を取ることができます。

[sample]$ ./arrP6_2.p6 3
The month No. 3 is Mar
[sample]$ ./arrP6_2.p6 7
The month No. 7 is Jul
[sample]$ ./arrP6_2.p6 八
Usage:
./arrP6_2.p6 <num>

 最後、「八」というサポートされていない引数をいじわるで渡してみると、なんと、Perl6エンジンによって勝手に「arrP6_2.p6というプログラムにはnumという引数を渡すんだよ」という使い方のマニュアルが表示されます。こいつは親切だ。
 シグネチャはサブルーチンについて研究するときにまた述べますが、サブルーチンの引数の型、個数、名前付き変数をサポートするもので、慣れると強力に便利そうです。で、上のプログラムは、メインプログラムを意味する特殊なサブルーチン名MAINを使って、引数$numを得ています。

 引数で時間を取りましたが、次は

my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;

です。これはPerl 5におけるqw演算子(quote words)

my @month = qw (DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);

と同じもので、

my @month = ("DUMMY", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");

の略記法です。なお、このように<>を使わずに従来型のリストを書くとき、カッコ()がいらなくなりました。

#! /usr/bin/env perl6
# arrP6_3.p6 -- Perl 6で配列操作3

sub MAIN (Int $num) {
 my @month = "DUMMY", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec";
 "The month No. $num is @month[$num]".say;
}

 これでも大丈夫です。

[sample]$ ./arrP6_3.p6 6
The month No. 6 is Jun

 これ、Perl 5で

#! /usr/local/bin/perl
# arrP5_bug.pl -- Perl 5で配列操作(間違い)

use strict;
use warnings;
use 5.10.0;

my $num = shift;

my @month = "DUMMY", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec";
say "The month No. $num is $month[$num]";

とか書いていたら、

[sample]$ ./arrP5_bug.pl 7
Useless use of a constant ("Jan") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Feb") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Mar") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Apr") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("May") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Jun") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Jul") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Aug") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Sep") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Oct") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Nov") in void context at ./arrP5_bug.pl line 10.
Useless use of a constant ("Dec") in void context at ./arrP5_bug.pl line 10.
Use of uninitialized value in concatenation (.) or string at ./arrP5_bug.pl line 11.
The month No. 7 is
[sample]$

と鬼のように怒られていたところです。

 最後が

"The month No. $num is @month[$num]".say;

です。

 Perl 5では、配列@monthのインデックス$numの(ゼロ始まりで$num番目の)配列要素は$month[$num]と書く、なぜなら配列要素はスカラーだから…と言って来ましたが、これがあっさり@month[$num]になりました。
 Perl 5でも、

#! /usr/local/bin/perl
# arrP5_bug2.pl -- Perl 5で配列操作(間違い2

use strict;
use warnings;
use 5.10.0;

my $num = shift;

my @month = qw (DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
say "The month No. $num is @month[$num]";

のように@month[$num]とついうっかり書いたとしても

[sample]$ ./arrP5_bug2.pl 3
Scalar value @month[$num] better written as $month[$num] at ./arrP5_bug2.pl line 11.
The month No. 3 is Mar
[sample]$

のように「スカラーの値@month[$num]は$month[$num]と書いたほうがいいね」と警告されるだけで、プログラム自体は動いていました。

 Perl 6で逆に

#! /usr/bin/env perl6
# arrP6_bug3.p6 -- Perl 6で配列操作(間違い3

sub MAIN (Int $num) {
 my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
 "The month No. $num is $month[$num]".say;
}

と書いてみたら、

[sample]$ ./arrP6_bug3.p6 10
===SORRY!=== Error while compiling /Users/query1000/Dropbox/kpc/sample/./arrP6_bug3.p6
Variable '$month' is not declared. Did you mean '@month'?
at /Users/query1000/Dropbox/kpc/sample/./arrP6_bug3.p6:6
------> "The month No. $num is ⏏$month[$num]".say;
[sample]$

のように「$month」という変数は定義されていないよ、'@month'と書きたかったんじゃないの」と言われます。これはコンパイルエラーです。

 ということで、Perl 6では配列要素は@month[$num]に覚え直しましょう。
 これは、長さが1個のスライスと考えればいいんじゃないでしょうか。
 Perl 6でもスライスは使えます。
 sub MAINを使わないやり方。

#! /usr/bin/env perl6
# arrP6_4.p6 -- Perl 6で配列操作4

my ($start, $end) = @*ARGS;
my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
"The months from $start to $end is @month[$start .. $end]".say;

[sample]$ ./arrP6_4.p6 3 7
The months from 3 to 7 is Mar Apr May Jun Jul

 sub MAINを使うやり方。

#! /usr/bin/env perl6
# arrP6_5.p6 -- Perl 6で配列操作5

sub MAIN (Int $start, Int $end) {
 my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
 "The months from $start to $end is @month[$start .. $end]".say;
}

[sample]$ ./arrP6_5.p6 3 7
The months from 3 to 7 is Mar Apr May Jun Jul

 3番目から7番目までの月はMar、Apr、May、Jun、Julである、と正しく表示されましたね。
 さて、二重引用符の中で配列要素@month[$num]とスライス@month[$start .. $end]は正しく展開されますが、@month(一年全体)はどうなるでしょうか。
 Perl 5では

#! /usr/local/bin/perl
# arrP5_2.pl -- Perl 5で配列操作(その2

use strict;
use warnings;
use 5.10.0;

my $num = shift;

my @month = qw (DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
say "The month No. $num is $month[$num]";
say "The whole year includes months: @month";

のように書くと

[sample]$ ./arrP5_2.pl 9
The month No. 9 is Sep
The whole year includes months: are DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
[sample]$

のように展開してくれました。(ゼロ月がDUMMYです、と言っているので少しおかしいですが!)

 しかし、

#! /usr/bin/env perl6
# arrP6_bug4.p6 -- Perl 6で配列操作(間違い4

sub MAIN (Int $num) {
 my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
 "The month No. $num is @month[$num]".say;
 "The whole year includes months: @month".say;
}

と書くと

[sample]$ ./arrP6_bug4.p6 1
The month No. 1 is Jan Feb
The whole year includes months: @month

のように、展開してくれなくなりました。

 これ、Perl 5は展開していたので、foo@example.comのようなメールアドレスを書くときに、いちいちfoo\@example.comとエスケープしなければいけないのが不便でしたが、Perl 6ではそれがなくなりました。
 では、展開して欲しいときはどうするか。こうします。

#! /usr/bin/env perl6
# arrP6_7.p6 -- Perl 6で配列操作7

sub MAIN (Int $num) {
 my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
 "The month No. $num is @month[$num]".say;
 "The whole year includes months: {@month}".say;
}

[sample]$ ./arrP6_7.p6 1
The month No. 1 is Jan
The whole year includes months: DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
[sample]$

 これは配列というより、二重引用符の中に{}で囲んで式を書くと値に展開される機能によるものです。

#! /usr/bin/env perl6
# arrP6_8.p6 -- Perl 6で配列操作8

sub MAIN (Int $num) {
 my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
 "The month No. $num is @month[$num]".say;
 "The whole year includes months: {@month}".say;
 "5 * 5 = { 5 * 5 } and 5 x 5 is { 5 x 5 }".say;
}

[sample]$ ./arrP6_8.p6 1
The month No. 1 is Jan
The whole year includes months: DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
5 * 5 = 25 and 5 x 5 is 55555

 なるほど〜。
 ところで、{@month}と書く代わりに、インデックスのないスライス@month[]と書いてもいいです。

#! /usr/bin/env perl6
# arrP6_9.p6 -- Perl 6で配列操作9

sub MAIN (Int $num) {
 my @month = <DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;
 "The month No. $num is @month[$num]".say;
 "The whole year includes months: @month[]".say;
 "5 * 5 = { 5 * 5 } and 5 x 5 is { 5 x 5 }".say;
}

[sample]$ ./arrP6_9.p6 6
The month No. 6 is Jun
The whole year includes months: DUMMY Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
5 * 5 = 25 and 5 x 5 is 55555
[sample]$

 この指標のないスライスをzen slice(禅スライス)というそうです。
 なんだそれ!

 いつものことながら散漫になったのでまとめます。

・配列@arrに文字列foo、bar、bazを入れるには

 Perl 5:
  @arr = ("foo", "bar", "baz");
  @arr = qw(foo bar baz);
 Perl 6:
  @arr = "foo", "bar", "baz";
  @arr = <foo bar baz>;

・配列要素@arrのインデックス$iの要素は

 Perl 5:
  $arr[$i] # @arr[$i]だと警告されるけど正しく動く
 Perl 6:
  @arr[$i] # $arr[$i]だとエラーになって動かない

・引数を取るには

 Perl 5:
  my $arg1 = shift;
  my ($arg1, $arg2) = @ARGV;
 Perl 6:
  my $arg1 = @*ARGS.shift;
  my ($arg1, $arg2) = @*ARGS;

  またはメインプログラム全体を

  sub MAIN ($arg1, $arg2) {
   …
  }

  でくるむ。

・スライス
  Perl 5:
   @arr[1, 2, 3]; # Perl 6と同じ
  Perl 6:
   @arr[1, 2, 3]; # Perl 5と同じ

・配列全体を二重引用符で展開する
  Perl 5:
   "The array \@arr is @arr"; # @arrを二重引用符に書くと展開してくれていた
  Perl 6:
   "The array @arr is {@arr}"; # @arrを二重引用符に書いても展開しなくなった。かわりに{}を使う
   "The array @arr is @arr[]"; # これでもいい。zen sliceと言う

 zen sliceすげえな!
 ではまた来週〜