Exegesis 2

by Damian Conway
May 15, 2001

(translated into Japanese by Tatsuhiko Miyagawa with permission of Damian Conway.)

exegesis: [名詞]. 文書、とくに聖書の解釈、解説。

この記事は Larry Wall の 一般向け ``Apocalypse(黙示録)'' に並行する記事の第一弾です。(2から始まっているのは、黙示と同期するためです)

ややこしいことは抜きにして、まずは Perl 6 を少し書いてみましょう:

        # bintree - binary tree demo program 
        # adapted from "Perl Cookbook", Recipe 11.15
        use strict;
        use warnings;
        my ($root, $n);
        while ($n++ < 20) { insert($root, int rand 1000) }
        my int ($pre, $in, $post) is constant = (0..2);
        print "Pre order:  "; show($root,$pre);  print "\n";
        print "In order:   "; show($root,$in);   print "\n";
        print "Post order: "; show($root,$post); print "\n";
        $*ARGS is chomped;
        $ARGS prompts("Search? ");
        while (<$ARGS>) {
            if (my $node = search($root, $_)) {
                print "Found $_ at $node: $node{VALUE}\n";
                print "(again!)\n" if $node{VALUE}.Found > 1;
            }
            else {
                print "No $_ in tree\n";
            }
        }
        exit;
        #########################################
        sub insert (HASH $tree is rw, int $val) {
            unless ($tree) {
                my %node;
                %node{LEFT}   = undef;
                %node{RIGHT}  = undef;
                %node{VALUE}  = $val is Found(0);
                $tree = %node;
                return;
            }
            if    ($tree{VALUE} > $val) { insert($tree{LEFT},  $val) }
            elsif ($tree{VALUE} < $val) { insert($tree{RIGHT}, $val) }
            else                        { warn "dup insert of $val\n" }
        }
        sub show {
            return unless @_[0];
            show(@_[0]{LEFT}, @_[1]) unless @_[1] == $post;
            show(@_[0]{RIGHT},@_[1])     if @_[1] == $pre;
            print @_[0]{VALUE};
            show(@_[0]{LEFT}, @_[1])     if @_[1] == $post;
            show(@_[0]{RIGHT},@_[1]) unless @_[1] == $pre;
        }
        sub search (HASH $tree is rw, *@_) {
            return unless $tree;
            return search($tree{@_[0]<$tree{VALUE} && "LEFT" || "RIGHT"}, @_[0])
                unless $tree{VALUE} == @_[0];
            $tree{VALUE} is Found($tree{VALUE}.Found+1);
            return $tree;
        }

これは Perl だよ、Jim。知ってたみたいだね。

プログラムの始まりは見慣れたものです:

        use strict;
        use warnings;
        my ($root, $n);
        while ($n++ < 20) { insert($root, int rand 1000) }

なにも新しいことはありません。実際、たくさんの新しい機能の説明をしてはいますが、このプログラム全体としては Perl 5 のコードにそっくりです。

これは別に驚くことではありません。Perl 6 は、大勢の熱心な Perl 5 プログラマの提案から生まれたものなのですから。

RFC 28 が提案しているように、Perl は間違いなく Perl のままなのです。

なにか宣言する変数は?

Perl 6 の変数宣言は上の $root$n の宣言のようにシンプルです。ただ、もっとしゃれた方法でも宣言できます:

        my int ($pre, $in, $post) is constant = (0..2);

ここで宣言している3つの変数は共通の型(int)とプロパティ(constant) を共有しています. 型つきレキシカル(Typed lexical)は Perl5 にもあった機能ですが、Perl のビルトイン型にも名前が付いたのははじめてです。

この型記述によって、コンパイラに $pre, $in, と $post には int のみが格納されることを伝えることができます。また、int が小文字なので、この変数の実装を最適化しても構わないことも伝えます。この記述をすると 変数をblessしたり、ランタイムでのプロパティを付加したりすることができなくなるためです。この約束をしたあとで、プログラム中で規則を破ってしまうと、コンパイル時もしくはランタイムのエラーになります(どちらになるかは、コンパイラがその違法行為を、静的にみつけられるかどうかによって決まります)。

もし bless やランタイムプロパティを犠牲にしたくないのであれば、このようにも書けます:

        my INT ($pre, $in, $post) is constant = (0..2);

こうすれば、3つの変数の最適化具合は下がりますが、いろんな機能をもった Perl のスカラ変数として扱えます。

こういった特殊なケースでは、intINT を使い分けても、実際にはあまり違いはありません。しかしながら、次のように書くと明確に差がでます:

        my int @hit_count is dim(100,366,24);

これと次とを比較した場合です:

        my INT @hit_count is dim(100,366,24);

この結果、100万近くのがっちりしたスカラ変数を、すっきりした int 型に置換できます。

La propriété c'est le vol

is constantis dim のような宣言はコンパイル時のプロパティ記述です。こうした特殊なプロパティが Perl6 ではスタンダードですが、好きなようにして構いません。is dim プロパティは、Perl に配列の次元数(固定です!)を伝えます。is constant プロパティは、変数が一度初期化されたら、決して代入や更新されないことを表します。

その上、constant プロパティは、変数をインライン化して最適化できるかもしれないという、コンパイラへのヒントになります。もちろん、これが実現可能になるのは、その変数を実際の変数と同じように扱わない場合のみです(リファレンスをつくったり、bless したりしないということ)。

is キーワードは、それがなくても意味が通る場合には省略可能です。よって、以下のようにも書けます:

        my int ($pre, $in, $post) constant = (0..2);

Larry は、areis の同意語として提供することも考えているようですので、こういう風に宣言してもいいかもしれません:

        my int ($pre, $in, $post) are constant = (0..2);

次に説明するis 演算子の重要な機能として、左側 のオペランドを返すことがあげられます。つまり:

        $submarine is Colour('yellow')

の文は$submarine として評価されます。'yellow' ではありません。

またまた同じもの

show を3回呼んでいるところは、Perl5 の時とまったく一緒です:

        print "Pre order:  "; show($root,$pre);  print "\n";
        print "In order:   "; show($root,$in);   print "\n";
        print "Post order: "; show($root,$post); print "\n";

幸運なことに、こういったケースはこの連載でたくさん目にすることになるでしょう。

無理しないでじっくり考えよう

こんな書き方に飽きたことはないでしょうか?:

        while (<>) {            # Perl 5 のイディオム
                chomp;
                ...

入力行がすべて自動的に chomp されたら素敵じゃないですか? Perl 6 では、それができます。入力ハンドルを表す $*ARGS というグローバル変数に、chomped プロパティをセットするだけでよいのです:

        $*ARGS is chomped;

これによって、あらゆるハンドルの読み込み(出力する入力参照)が、自動的に chompされた文字列を返すようになります。もちろん、他の区切りグローバル変数と同様、$/ は Perl 6 から追放されました。よって chomp される trailing character は、$/の代わりに、そのハンドル自身の insep (input separator)プロパティによって指定されます。

$*ARGS にあるアスタリスクは、この変数が特殊なグローバルの名前空間のものであることを示しています。もしこのアスタリスクが省略されても、同名のレキシカルもしくはパッケージ変数を宣言しない限り、特殊なグローバルの名前空間のものになります。わかりやすければ、* を ``スタンダード''と読んでもいいでしょう。

ところで、$*ARGS と呼ばれるのは、これによって Perl 6 プログラムの引数(arguments)にアクセスできるからです(Perl 5 のARGV ファイルハンドルがプログラムの ... うーんと ...argumentv にアクセスできるのと同じです)。

出力する入力

このプログラムの Cookbookのオリジナルバージョンでは、次の行はこうなっていました:

        for (print "Search? "; <>; print "Search? ") {

この部分は Perl 5 では納得できる解法がない象徴的なシチュエーションです。つまり、入力プロンプトを出して、$_ に読み込んで、というのを EOFまで繰り返します。Perl 6では、ついにきれいな方法ができました。また別のプロパティです:

        $ARGS prompts("Search? ");
        while (<$ARGS>) {

まず気づくのは、ダイアモンド演算子の死が大げさに報告されているということでしょう。そうです、第2の黙示録がダイアモンド演算子の死を予言していたにもかかわらず、裁定その2がそれから適用され、カギカッコは生きているのです!

もちろん、Perl 6 では扱いが微妙に異なり、ハンドルオブジェクト(通常は変数に格納されています)がカッコの中に必須になっています。ただ、それは Perl 5 でもすでに可能です。

ところで、プロンプトについてはどうでしょう? そう、Perl 6 の解法では、入力ハンドルは、関連した文字列をプロパティに持ち、データを読み込む前にそれを print することができます。

ちょっと待った!、 異議を唱えるのが聞こえました. 入力ハンドルが出力をするのか??? 実際、長いことハンドルをそのように使ってきたはずです。多くの言語では、標準入力から読み込む時に、標準出力のフラッシュが必ず最初に行われます。これによって、:

        print "nuqneH? ";
        $request = <>;

こんな感じのコードを書いても、改行で終っていないにもかかわらず、読み込み前にプロンプトが正しく出力されるのです。

つまり入力と出力のメカニズムは、すでに秘密の不適切な関係を持っているのです。 ここで変わったことといえば、入力ハンドルはバッファをフラッシュする前に、ちょっとした文字列を表示できるようになったことだけです。 これは prompts プロパティによってなされます。もし入力ハンドルがそのプロパティを持っている場合、入力を読み込む直前に、その値が$*OUT に書かれます。よってこのコードは:

        for (print "Search? "; <>; print "Search? ";) {         # Perl 5 (or 6)

このように書き直せます。

        $ARGS prompts("Search? ");                              # Perl 6
        while (<$ARGS>) {

技術的には、もちろんこうあるべきです:

        $ARGS is prompts("Search? ");

でも、これはちょっと気持ち悪いでしょう。ありがたいことに is は、まさにこんな感じのコンテキストでは、省略可能です。

注目すべきは、is 演算子は(is が省略されていても!)左側のオペランドを返すので、もっとエレガントにできます:

        while (<$ARGS prompts("Search? ")>) {

実際、このワンライン版の方が好まれやすいでしょう。prompts プロパティの値はループのどこかで変更されうるし、また毎回イテレーションのたびにリセットされますから。

プロンプトメカニズムの正確なセマンティクスはまだ明確になっていないので、サブルーチンリファレンスを使ってダイナミックにプロンプトを生成する(毎回入力を読み込む前に、ハンドルがそのサブルーチンを呼び、返り値を表示する)こともできるかも知れません。

どこかで会いませんでしたっけ? (part 1)

要求を出して、値を読み込んだら、探索とレポートのコードはほとんど見慣れたものです:

            if (my $node = search($root, $_)) {
                print "Found $_ at $node: $node{VALUE}\n"
                print "(again!)\n" if $node{VALUE}.Found > 1;
            }
            else {
                print "No $_ in tree\n"
            }
        }

唯一潜んでいるPerl6イズムは、ユーザ定義の Found プロパティを使っていることです。

$node{VALUE}.Found は通常はメソッドコールになります (Perl 6 では -> ではなく、. と書きます). しかし$node{VALUE} は bless されていない普通の int ですので、Found というメソッドはありません。よって Perl はこの呼び出しをプロパティへのクエリと見なし、対応するプロパティ(へのエイリアス)を返します。

あれをとって! あれも!

Perl 6 では、サブルーチンが -- 必要ならば --パラメータリストを指定できるようになります (Perl 5 で可能な、「悪くはないけど誤解されやすい」引数のプロトタイプとは対象的です)。

たとえば、insert サブルーチンは、2つのパラメータをとると宣言しています:

        sub insert (HASH $tree is rw, int $val) {

最初のパラメータによって、第1引数はハッシュリファレンスで、その値がレキシカル変数の $tree に格納されることが記述されています. 第1引数をハッシュリファレンスと定義することによって、他の方法(つまり、そこにサブルーチン呼び出しを入れたり、明示的に配列リファレンスを渡したり、など)で使おうとした場合には、コンパイル時にキャッチされて罰せられることになります。

デフォルトでは、名前付きパラメータは@_ の要素とは 異なるということを理解することが大事です。具体的にいうと、それぞれの引数は対応するパラメータにリファレンス渡しされる(効率のために)にもかかわらず、パラメータの値そのものは自動的に constant として宣言されます。つまりそれに別の値を代入しようとすると、すべてコンパイルエラーになります。これは人々が偶然「自分の足を撃つ」ことを減らすことを意図しています。

もちろん、これは Perl ですから、じっと中足骨に狙いを定めれば、足を撃つことが出来ます。もともとの引数に反映されるような、名前付きパラメータへの代入を許可するには、そのパラメータを標準の rw (read-write) プロパティつきで宣言します。 そうすると、代入可能な、もともとの引数へのエイリアスになり、この例ではこの変数に意味を持たせることが出来ます。(腐ったバックスラッシュはもういらない参照)。

引数の配列 @_ は Perl 6 でも利用可能ですが、それはサブルーチンを Perl 5 風に、パラメータリストなしで宣言した場合のみです。(古き良き外観参照)

insert の第2引数は int 値をとるように定義されています。 INT ではなく int を使っているので、これもまた明示的に、これに対して変なことを(少なくとも、insert の内側では)しないと約束しています。コンパイラはこの情報を使って、サブルーチンのコードを最適化することができるかもしれません。

印は一生のもの、値の型だけのためではない

遠い昔、地球がまだ新しく Perl が若くて清純だった頃、変数にくっついている印のタイプ ($, @, や %)が、その変数がどう評価されるかを記述していました。例えば:

        print $x;                       # $x はスカラ
        print $y[1];                    # $y[1] はスカラ
        print $z{a};                    # $z{a} はスカラ
        print $yref->[1];               # $yref->[1] はスカラ
        print $zref->{a};               # $zref->{a} はスカラ
        print @y;                       # @y はリスト
        print @y[2,3];                  # @y[2,3] はリスト
        print @z{'b','c'};              # @z{'b','c'} はリスト
        print @{$yref}[2,3];            # @{$yref}[2,3] はリスト
        print @{$zref}{'b','c'};        # @{zyref}{'b','c'} はリスト
        print %z;                       # %z はハッシュ

実際に参照される変数の型に関係なく、アクセスされるときの先頭の $ によって、結果がスカラになることになっていました。@ はリストになり、% はハッシュになったのです。

しかしそのとき、OO ヘビが庭に侵入し、Perl種にサブルーチンとメソッドコールの苦い果物を与えました:

        print $subref->();
        print $objref->method();

こうして、先頭の $ は返り値の型に関係なくなりました。 そして世界中の初心者向けの Perl 教室で、嘆きと歯ぎしりが起きました。

Perl 6 は我々を優雅な選ばれし民 -- 異なった優雅さではありますが -- の座に戻してくれます。そこでは、それぞれの変数型は 一つの印を持ち、迷うことはありません。

Perl 6 では、スカラは つねに 先頭に $ を持ち, 配列は つねに 先頭に @ を (配列の要素やスライスにアクセスする時も)持ち、ハッシュは つねに 先頭に % を(要素やスライスにアクセスする時も)持ちます。

言い替えれば、印はもう、結果としての値の型を(ときどき)示すのではないのです。代わりに、いじっている変数が、どの変数であろうとも、まさにどの型なのかを(つねに)示すのです。

insert サブルーチンの中に、この新しい構文の例をいくつもみることができます。もっともわかりやすいのが、サブルーチンの最初にある、空の部分木を生成する箇所でしょう:

            unless ($tree) {
                my %node;
                %node{LEFT}   = undef;
                %node{RIGHT}  = undef;
                %node{VALUE}  = $val

%node ハッシュの要素にアクセスしているにもかかわらず、変数の先頭は % のままで、ハッシュアクセスのブレイス({})が変数名の後ろについています。

同じように、配列の要素にアクセスするには、配列アクセス用のブラケット([])を変数名のうしろに付けて: @array[1] とします。これは Perl 5 の構文から大きく逸脱する部分です。Perl 5 では、@array[1] は要素が1つの、@array のスライスでした。 Perl 6 では、これは単一要素へのダイレクトアクセスになります(スライスは含まれません)。

もちろん、これはすなわち、Perl 6 には改訂された配列スライス用のセマンティクスが必要なことを意味しています。Larry はこの機会に Perl のスライス機能を増強して、多次元の配列の任意のスライスやダイスに備えようとしています。しかし、それはまだ先の黙示録に書かれます。

いまのところは、これを知っていれば十分でしょう。スクエアブラケットの中に単一スカラを書けば、単一要素のルックアップになる。ブラケットの中にリストを書けば、スライスになる。

どこかで会いませんでしたっけ? (part 2)

最後の、%node の要素への代入はひとひねりがあります。 代入される値 (のコピー) は Found プロパティも割り当てられて、その値が0に初期化されています:

                %node{VALUE}  = $val is Found(0);

もう一度書くと、これが動作するのは、プロパティが is によってセットされるときは、演算の結果は左側のオペランド(この場合 $val)となり、プロパティの新しい値の方ではないからです。

実際、さきほども見ましたが、以下のような構文が正しく動作するのはそういった理由です:

        while (<$ARGS prompts("Search? ")>) {

$ARGS prompts("Search? ") という文はハンドルのプロンプトをセットし、$ARGS を返します。これがダイアモンド演算子のオペランドになり、結果としてプロンプト-読み込み操作がそのハンドルによって行われます。

腐ったバックスラッシュはもういらない

いったん新たな%node が初期化されると、そのリファレンスは第1引数として渡す用の変数に代入される必要があります。(もしこれでよくわからなかったら、 Object Oriented Perl のセクション 12.3.2 にある、このツリー操作テクニックの詳細な解説を見てください。)

Perl 5では、もともとの引数を変更するには、$_[0](つまり Perl 6 では @_[0]) への代入が必須でした。しかしここでは、$treerw として宣言したので、これに直接代入することで、もともとの引数に適切に変更を加えることが出来ます:

                $tree = %node;

ありゃ、 (と思っているでしょう)、 古典的な失敗にひっかかってしまいました。スカラコンテキストでは、ハッシュは、利用しているバケットの比率として評価されるのです!

Perl 5 ではそうでしょう、しかしながら Perl 6 では、この「ほとんど使えない」動作は、パウダーかつらや、流行遅れのものや、DSL プロバイダと同じ道をたどりました。代わって、スカラコンテキストで評価すると、ハッシュ(や配列)は自身のリファレンスを返します。よって、上のコードは正しく動作するのです。

わかった、 (不思議に思っているでしょう), もし配列もそうなら、どうやって配列の長さを取得するの??? その答えは、数値コンテキストでは、配列リファレンスが、今度は配列の長さに評価されます。よって、Perl 5 のコードを翻訳すると:

        while (@queue > 0) {    # @queue をスカラで評価すると長さになる

これは:

        while (@queue > 0) {    # @queue をスカラで評価するとリファレンスになる
                                # 配列リファレンスを数値で評価すると長さになる

同様に、ブーリアンコンテキストでは、配列は、要素を持っていれば true として評価されます。よって Perl 5 のコードを翻訳すると:

        while (@queue) {    # @queue をスカラで評価すると長さになる

これは:

        while (@queue) {    # @queue をブーリアンで評価すると、空でなければ true となる

抜け目がないでしょう?

あなたは %node{VALUE}, でもわたしは $tree{VALUE}

新しいノードを積み込んだら、%node{VALUE}'VALUE' エントリにアクセスします。$tree%node へのリファレンスを保持しているのですから、同じエントリにアクセスする方法が必要です。

Perl 5 ではこうなるでしょう:

        $tree->{VALUE}        # Perl 5 式 ハッシュリファレンス $tree のエントリへのアクセス

そして、-> は Perl 6 では . と書くのですから、Perl 6 ではこうなります:

        $tree.{VALUE}         # Perl 6 式 ハッシュリファレンス $tree のエントリへのアクセス

しかしながら、ハッシュへのダイレクトアクセスは、いまやまったく異なる印を使って、%node{VALUE} と書かれます。よって、. がなくても曖昧にはならないので、省略可能です:

        $tree{VALUE}          # Perl 6 式 ハッシュリファレンス $tree のエントリへのアクセス

そしてこれが、ハッシュリファレンスにアクセスする通常の書き方です:

            if    ($tree{VALUE} > $val) { insert($tree{LEFT},  $val) }
            elsif ($tree{VALUE} < $val) { insert($tree{RIGHT}, $val) }
            else                        { warn "dup insert of $val\n" }
        }

これは実際、一見して感じるより、まったくまぎらわしくないのです。 例えば、どこかで会いませんでしたっけ? (part 1)に戻ってみて、ここで

        if (my $node = search($root, $_)) {
            print "Found $_ at $node: $node{VALUE}\n"

この新しい構文がすでに使われていたことに気づきましたか?

Perl 5 では、これは(非常によくある)エラーでした。2行目は、実際は %{$node} のエントリが欲しいのに、%node のエントリを print しようとしてエラーになるのです。 しかし Perl 6 では、「意図した通りに」動くのです。

そしてもちろん、その他のリファレンスへのアクセスも、同様に. を省略できます: $arr_ref[$index]$sub_ref(@args) です。

簡単な変換表を用意しました:

        Access through...       Perl 5          Perl 6
        =================       ======          ======
        スカラ変数              $foo            $foo
        配列変数                $foo[$n]        @foo[$n]
        ハッシュ変数            $foo{$k}        %foo{$k}
        配列リファレンス        $foo->[$n]      $foo[$n] (or $foo.[$n])
        ハッシュリファレンス    $foo->{$k}      $foo{$k} (or $foo.{$k})
        コードリファレンス      $foo->(@a)      $foo(@a) (or $foo.(@a))
        配列スライス            @foo[@ns]       @foo[@ns]
        ハッシュスライス        @foo{@ks}       %foo{@ks}

古き良き外観

サブルーチン show はパラメータリストのオプション機能を説明しています。ここでは、パラメータ指定をすべて省略して、古き良き、見慣れた``任意の数の引数をとって、それを全部@_に入れる''式のセマンティクスを利用しています。

実際、DWIM (Do What I Mean) っぽい配列アクセスの構文をのぞけば、サブルーチン show は生の Perl 5 です:

        sub show {
            return unless @_[0];
            show(@_[0]{LEFT}, @_[1]) unless @_[1] == $post;
            show(@_[0]{RIGHT},@_[1])     if @_[1] == $pre;
            print @_[0]{VALUE};
            show(@_[0]{LEFT}, @_[1])     if @_[1] == $post;
            show(@_[0]{RIGHT},@_[1]) unless @_[1] == $pre;
        }

そしてこれは、5 から 6 へ移行する時に通常経験するものになるでしょう: Perl はずっと Perl であり続けます ... わずかに成長しながら

もちろん、サブルーチン show は、とにもかくにも、ほどよくファンキーな Perl ですから、もし左右均等に部分木を繰り返し入れかえるのが、理想的な保守状態でないなら、、Perl の新しい case 文の出番になるでしょう。

しかし、これは黙示録その4 まで明らかにされませんから、もしこの 小さな赤い光が見えるのなら....<FLASH>...ありがとう。

わたしを探して

サブルーチン search のパラメータリストはPerl の新旧のセマンティクスの雑種となっていて興味深いです:

        sub search (HASH $tree is rw, *@_) {

両方のパラメータは明示的に宣言されていますが、2つめの宣言(*@_) によって、残りのパラメータは@_ に集められます。 ここで @_ に関して 不思議な(magical) ことは一つもありません: もし2つめの宣言が *@others の場合、残りの引数は @others に入ります。

2つめのパラメータのアスタリスクは、対応する引数の順番が、ただのリストコンテキストであることを Perl 6 に伝えています。つまり、引数はすべて単一のリストとして扱われ、対応するパラメータの値に代入されることを示しています。これは Perl 5 の @ プロトタイプと等価です。

対して、@param のパラメータ宣言は、Perl 5 の \@ プロトタイプと等価で、対応する引数として配列変数が明示的に必要です。

気づいたかもしれませんが、@_ の引数を 2番目 から集めているので、探しているパラメータ (つまり第2引数) は @_[0] として参照されています。@_[1]ではありません:

            return search($tree{@_[0]<$tree{VALUE} && "LEFT" || "RIGHT"}, @_[0])
                unless $tree{VALUE} == @_[0];

どこかで会いませんでしたっけ? (part 3)

search の後ろから2行目は、Perl 6 バリバリの箇所です。うまく目的のノードにたどりついて、それを返します。 しかし Found プロパティをインクリメントさせる必要もあり、こんな感じでやっています:

            $tree{VALUE} is Found($tree{VALUE}.Found+1);

この部分からプロパティにアクセスする3つの方法のうち2つがよくわかります: read-write の . 構文と write-only の is 演算子です。

プロパティがメソッドのようにアクセスされる場合、新しい値を引数に渡すことによってセットできます。値が渡されるかどうかにかかわらず、演算子の結果はプロパティへのエイリアス(つまり lvalue)です。つまり、値の Found プロパティはこんな感じでもインクリメントできます:

        $tree{VALUE}.Found($tree{VALUE}.Found+1);

もしくは、このように:


        $tree{VALUE}.Found++;

一方、is 構文 はプロパティのセットしか出来ません。これは is 演算は左側のオペランド (プロパティを持っているもの) を返し、プロパティそのものの値は返さないためです。しかしながら、これは return 文の最後ぎりぎりでプロパティをセットするときにかなり便利です:

        return $result is Verified;

もう一つよく使われそうなのは、0でtrue や、0でないfalse を返すといったときでしょう:

        sub my_system ($shell_command) {
                ...
                return $error is false if $error;
                return 0 is true;
        }

プロパティにアクセスする3つめの方法は、メタプロパティ prop によって、すべてのプロパティを含んだハッシュのリファレンスを取得する方法です:

        $tree{VALUE}.prop{Found}++;

この機能を使って、その変数がもつプロパティをすべてリストすることもできます:

        for (keys %{$tree.prop}) {
            print "$_: $tree{VALUE}.prop{$key}\n";
        }

ところで、黙示録その2 では、Larry はメタプロパティ prop のことを、おどけて btw といっていますが、現代の治療技術のおかげで、克服したようです。

初期のテーマにもどる

この記事はさまざまな、Perl 6 の提供する重要な新機能を解説しました。しかし、新しいからといっておびえることはありません。Perl はいつだって、あなたのレベルで、自分にしっくりくるスタイルで、コードを書く方法を提供してきました。それが変わることはありません。もし Perl 5 のスタイルがあなたにとってベストだとしても。

ここで扱った新機能のほとんどは、任意のものですから、使わないと決めても、同じプログラムを Perl 5 にとても近い形で書くことが出来ます。こんな感じで:

        use strict;
        use warnings;
        my ($root, $n);
        while ($n++ < 20) { insert($root, int rand 1000) }
        my ($pre, $in, $post) = (0..2);
        print "Pre order:  "; show($root,$pre);  print " \n";
        print "In order:   "; show($root,$in);   print " \n";
        print "Post order: "; show($root,$post); print " \n";
        for (print "Search? "; <$ARGS>; print "Search? ") {
            chomp;
            if (my $node = search($root, $_)) {
                print "Found $_ at $node: $node{VALUE}\n";
                print "(again!)\n" if $node{FOUND} > 1;
            }
            else {
                print "No $_ in tree\n";
            }
        }
        exit;
        #########################################
        sub insert {
            unless (@_[0]) {
                @_[0] = { LEFT  => undef, RIGHT => undef,
                          VALUE => @_[1], FOUND => 0,
                        };
                return;
            }
            if    (@_[0]{VALUE} > @_[1]) { insert(@_[0]{LEFT},  @_[1]) }
            elsif (@_[0]{VALUE} < @_[1]) { insert(@_[0]{RIGHT}, @_[1]) }
            else                         { warn "dup insert of @_[1]\n"  }
        }
        sub show  {
            return unless @_[0];
            show(@_[0]{LEFT}, @_[1]) unless @_[1] == $post;
            show(@_[0]{RIGHT},@_[1])     if @_[1] == $pre;
            print @_[0]{VALUE};
            show(@_[0]{LEFT}, @_[1])     if @_[1] == $post;
            show(@_[0]{RIGHT},@_[1]) unless @_[1] == $pre;
        }
        sub search {
            return unless @_[0];
            return search(@_[0]{@_[1]<@_[0]{VALUE} && "LEFT" || "RIGHT"}, @_[1])
                unless @_[0]{VALUE} == @_[1];
            @_[0]{FOUND}++;
            return @_[0];
        }

実際、Pure Perl5 から変わっているのは、40 文字 (1779文字中) です。そして、その大部分が、配列要素のルックアップの先頭が、$ でなくて @ になっていることだけです。

p52p6 トランスレータなしで、98% の backward compatibility です ... すばらしい!

Links in this document:
Cookbook: http://www.oreilly.com/catalog/cookbook/
Object Oriented Perl: http://www.manning.com/conway/

You can get the original version at perl.com, http://www.perl.com/pub/2001/05/08/exegesis2.html