Perl 6およびRakudo:2009ノート

Perl 6およびRakudo、Perl6仕様をサポートするコンパイラーの1つに関する一連の記事。 この記事は2009年のノートからまとめられています。



楽堂をインストールする



現時点では、Perl 6の不完全な実装がいくつかあります。それらの最も完全なものは、 Rakudoコンパイラです( ダウンロード )。



Gitユーザーは、次のコマンドを使用してコピーを作成できます。

$ git clone git://github.com/rakudo/rakudo.git $ cd rakudo $ perl Configure.pl --gen-parrot --gen-moar --gen-nqp --backends=parrot,jvm,moar $ make $ make install
      
      







または、 github.com / rakudo / rakudo / tree / nomからダウンロードして、ソースからコンパイルすることもできます



Windows用の既製のバイナリインストーラーがあります。 インストーラーのバージョンとソースコードはこちらから入手できます。



perl6コマンドを実行すると、さまざまな言語コマンドで遊ぶことができるREPL環境になります。



 $ perl6 > say "Hello world!"; Hello world! > say (10/7).WHAT (Rat) > say [+] (1..999).grep( { $_ % 3 == 0 || $_ % 5 == 0 } ); 233168
      
      







「>」で始まる行はコマンドであり、残りはすべてシステム応答です。 最初の例は、単純なsayステートメントです。 2番目は有理数を作成し、そのタイプ(Rat)を要求します。 3番目のものは、1から999までの数字のリストを取得し、3または5で割り切れない数字を除外し、それらを加算して結果を表示します。



フォーマットの美しさ



この記事では、.fmtメソッドを見ていきます。



sprintf命令に精通している場合、.fmtを扱う方が簡単です。 そうでない場合、または使用方法を忘れた場合は、perldocをお読みください。 ただし、深く掘り下げるのではなく、参照してください。



.fmt これを使用して文字列と整数をフォーマットする方法をいくつか示します。



  say 42.fmt('%+d') # '+42' say 42.fmt('%4d') # ' 42' say 42.fmt('%04d') # '0042' say :16<1337f00d>.fmt('%X') # '1337F00D'
      
      







良いですが、今のところこれはsprintfを書くための短い方法です。 ただし、配列(より正確にはリスト)で使用すると、このメソッドの動作が異なることがわかります。



  say <  >.fmt #    say <10 11 12>.fmt('%x') # 'abc' say <1 2 3>.fmt('%02d', '; ') # '01; 02; 03'
      
      







そして、ハッシュ(マッピング)での使用は次のとおりです。



  say { foo => 1, bar => 2 }.fmt # 'foo 1 # bar 2' say { '' => 85, '' => 75 }.fmt('%s   %d ') # '   85  #    75  ' say { '' => 1, '' => 2, '' => 3 }.fmt('%s', ' -- ') #  --  -- 
      
      







ただし、ハッシュの場合、発行の順序は上記と異なる場合があります。 ペア用の.fmtもありますが、ハッシュと同じように機能します。 .fmtは、値または値の配列を変更し、目的の形式に変換するための便利なツールです。 sprintfに似ていますが、配列でも機能します。 唯一のマイナスは、コードが読みやすいということです。 書き込み専用言語としてのPerlの評判を回復するために、クリスマスツリーの1行描画として新年のプレゼントを用意しました。



 $ perl6 -e 'say " "x 9-$_,"#"x$_*2-1 for 0..9,2 xx 3' # ### ##### ####### ######### ########### ############# ############### ################# ### ### ###
      
      







Windowsのオプション(他の引用符が必要):

 > perl6.exe -e "say ' 'x 9-$_,'#'x$_*2-1 for 0..9,2 xx 3"
      
      







静的型付けとマルチサブ



Perl 5では、$スカラー変数に参照または値を含めることができます。 値は何でも構いません-整数、文字列、非整数、日付。 明快さを犠牲にしての柔軟性。



Perl 6は静的型付けを導入しています。 特定のタイプの変数が必要な場合は、初期化中にこのタイプを指定します。 たとえば、整数を含む変数は次のとおりです。



 my Int $days = 24;
      
      







他のタイプの例:



  my Str $phrase = " !"; my Num $pi = 3.141e0; my Rat $other_pi = 22/7;
      
      







古い形式の変数を使用するには、タイプを指定しないか、タイプAnyを指定します。



この章の2番目のトピックはマルチサブです。 これは、異なる場合に同じ名前を使用してプロシージャをオーバーロードする機会です。 以下に例を示します。



 multi sub identify(Int $x) { return "$x –  ."; } multi sub identify(Str $x) { return qq<"$x" –  .>; } multi sub identify(Int $x, Str $y) { return " $x   \"$y\"."; } multi sub identify(Str $x, Int $y) { return " \"$x\"    $y."; } multi sub identify(Int $x, Int $y) { return "   - $x  $y."; } multi sub identify(Str $x, Str $y) { return "  - \"$x\"  \"$y\"."; } say identify(42); say identify("   !"); say identify(42, "    !"); say identify("   !", 42); say identify("   !", "!"); say identify(42, 24);
      
      







結果:



 42 –  . "   !" –  .  42   "    !".  "   !"    42.   - "   !"  "!".    - 42  24.
      
      







テスト中



perl-modulesの作成者は、世界にリリースするモジュール、一連のテストの提供に慣れています。 この伝統は、特定の指示によりperl 6で維持されています。



perlでテストを記述する古典的な方法は、Test Anything Protocolを使用してデータを出力することです。 ただし、手動で行う必要はありません-モジュールを使用できます。



階乗関数があるとします:



  sub fac(Int $n) { [*] 1..$n }
      
      







これまでどのように機能するかは問題ではありません。正しく機能するかどうかを知りたいだけです。 確認しましょう:



 use v6; sub fac(Int $n) { [*] 1..$n } use Test; plan 6; is fac(0), 1, 'fac(0) '; is fac(1), 1, 'fac(1)  '; is fac(2), 2, 'fac(2)  '; is fac(3), 6, 'fac(3)  '; is fac(4), 24, 'fac(4)  '; dies_ok { fac(' ,    ') }, '    ';
      
      







実行:



  $ perl6 fac-test.pl 1..6 ok 1 - fac(0)  ok 2 - fac(1)  ok 3 - fac(2)  ok 4 - fac(3)  ok 5 - fac(4)  ok 6 -     
      
      







詳細:テストを使用します。 テストモジュール、プラン6をロードします。 6つのテストの開始を発表します。 次に、「内容」、「受信予定」、「説明」の形式で5行があります。 is()は文字列を比較し、整数は自動的に文字列に変換されるため、すべてうまくいきます。



dies_ok {$ some_code}、$ descriptionの最後で、整数以外の引数で関数を呼び出すとエラーが発生することを確認します。



テスト出力は、6つのテストが実行されていることを示し、各行にテスト結果(OK-合格した場合、OK-失敗した場合)、テスト番号、および説明を表示します。



多数のテストを開始するときに、すべてを詳細に表示するのではなく、結果を表示する必要があります。 proveコマンドはまさにそれを行います:



  prove --exec perl6 fac-test.pl fac-test.pl .. ok All tests successful. Files=1, Tests=6, 11 wallclock secs ( 0.02 usr 0.00 sys + 10.26 cusr 0.17 csys = 10.45 CPU) Result: PASS
      
      







テストファイルを別のt /ディレクトリに追加し、拡張子が.tのディレクトリのすべてのファイルに対して証明を再帰的に実行するのが一般的です。



  prove --exec perl6 -rt
      
      







この行をMakefileに入れると、単にmake testと入力してテストを実行できます。



メタ演算子



先に、階乗関数の興味深い実装を見ました:



  sub fac(Int $n) { [*] 1..$n }
      
      







しかし、それはどのように機能しますか? Perl 6には、より強力になっている既存の演算子を変更するいくつかのメタ演算子があります。 角括弧は、リストのすべての要素間の括弧内に指定された演算子を配置する、reduceメタ演算子です。 例えば



  [+] 1, $a, 5, $b
      
      







と同じ意味



  1 + $a + 5 + $b
      
      







したがって、リストの要素を簡単に要約できます。



  $sum = [+] @a; #    @a
      
      







ほとんどすべての演算子は角括弧内に配置できます。



  $prod = [*] @a; #   @a $mean = ([+] @a) / @a; #    @a $sorted = [<=] @a; # ,   @a    $min = [min] @a, @b; #       @a  @b
      
      







したがって、階乗では、式[*] 1 .. $ nは、1から$ nまでの乗算リスト要素の値を取ります。



別のメタ演算子はハイパーです。 演算子の隣に「または」(または対応するASCIIの>>と<<)を配置することにより、リストのすべての要素で機能するようにします。 たとえば、次の式は、@ a要素と@b要素をペアで追加した結果を@cにします。



  @c = @a »+« @b;
      
      







Perl 5では、次のように書かなければなりません。



  for ($i = 0; $i < @a; $i++) { $c[$i] = $a[$i] + $b[$i]; }
      
      







hyperは、ユーザー定義演算子を含むさまざまな演算子で使用されます。



  #    @xyz  1 @xyz»++ #    @x      @a  @b @x = @a »min« @b;
      
      







配列の代わりに、スカラーを使用できます。



  #    @a  3.5 @b = @a »*» 3.5; #    @x  $m   $b @y = @x »*» $m »+» $b; #    @x @inv = 1 «/« @x; #   @last @first   @full @full = (@last »~» ', ') »~« @first;
      
      







もちろん、reduceとhyperは組み合わせることができます:



  #    @x $sumsq = [+] ( @x »**» 2);
      
      







X(クロス)、R(リバース)、S(シーケンシャル)など、さらに多くのメタ演算子があります。 一般的に、+ =、* =、〜=などの演算子は、すでにメタ形式の演算子であり、それに等号が追加されています。



  $a += 5; #  ,   $a = $a + 5; $b //= 7; #  ,   $b = $b // 7; $c min= $d; #  ,   $c = $c min $d;
      
      







ハイパースペースに入る



メタ演算子の研究を続ける前に、美しくフォーマットされたリストを表示する補助関数lsayを紹介します。 を介して定義すると、REPL環境で使用できます。



 our sub lsay(@a) { @a.perl.say }
      
      







簡単なものから始めましょう。同じ長さの2つのリストを追加します。



 > lsay (1, 2, 3, 4) <<+>> (3, 1, 3, 1) [4, 3, 6, 5] > lsay (1, 2, 3, 4) >>+<< (3, 1, 3, 1) [4, 3, 6, 5]
      
      







リストの長さが同じ場合、両方の記録オプションは同じです。 しかし、長さが異なる場合:



 > lsay (1, 2, 3, 4) <<+>> (3, 1) [4, 3, 4, 5] > lsay (1, 2, 3, 4) >>+<< (3, 1) #  
      
      







ルールは次のとおりです。ハイパーオペレータの鋭い端が示すものは、それが他の端にあるものより短い場合、拡張することができます。 リストの最後の要素を繰り返すことにより、更新が行われます。 「鈍い」エンドポイントが指すものは更新の対象ではありません。 たとえば、左側のみ(<< + <<)、右側のみ(>> + >>)、両側(<< + >>)、または側面なし(>> + <<)の場合、すべての組み合わせが可能です。 ) 単一のスカラーも拡張できます。



 > lsay (1, 2, 3, 4) >>+>> 2 [3, 4, 5, 6] > lsay 3 <<+<< (1, 2, 3, 4) [4, 5, 6, 7]
      
      







これらは、ハイパーオペレータを使用する基本です。 また、後置演算子と前置演算子とともに使用できます。



 > lsay ~<<(1, 2, 3, 4) ["1", "2", "3", "4"] > my @a= (1, 2, 3, 4); @a>>++; lsay @a; [2, 3, 4, 5]
      
      







それも可能です:



 > lsay (0, pi/4, pi/2, pi, 2*pi)>>.sin [0, 0.707106781186547, 1, 1.22464679914735e-16, -2.44929359829471e-16] > lsay (-1, 0, 3, 42)>>.Str ["-1", "0", "3", "42"]
      
      







この場合>>。 リスト内の各アイテムでメソッドを呼び出します。



配列を書きたい場合>>。言うなら、それ以上は必要ありません。 ハイパー演算子の使用は、操作を並列に実行できることを意味し、リスト上の操作の順序は固定されていません。



ハイパー演算子は、組み込み演算子だけで機能しません。 独自の演算子を定義できます。また、演算子も使用できます。 インプレース演算子で動作します(ただし、まだ動作しません)。たとえば、命令@a >> / = >> 2は配列全体を2で除算する必要があります。これらは多次元リスト、ツリー、ハッシュで動作します。 ハイパー演算子の使用の興味深い例は、Vectorクラスです。

github.com/LastOfTheCarelessMen/Vector/blob/master/lib/Vector.pm

これは、単一サイクルのない多次元ベクトルの実装を表します。



サイクル



プログラマーは、ループがどれほど便利かを知っています。 ループを使用する一般的な例は、配列を走査するためのforeachループです。 これはまさにPerl 5で使用した種類のキーワードであり、Cスタイルを連想させるために使用することもできます。



Perl 6は異なります。



forがリストの受け渡しに使用されるようになりました。 foreachはもう存在せず、Cスタイルにはwordループが使用されます。 当面は、この言語の新しい柔軟で強力な機能である次のもののみを考慮します。



 for 1, 2, 3, 4 { .say }
      
      







すぐに目立つのは、リストの周りに括弧がないことです。 通常、Perl 6では、Perl 5よりも少ないブラケットが必要です。Perl5のようなデフォルト変数は$ _です。 変数を指定せずにメソッドを呼び出すとは、メソッド$ _、つまり、この場合は$ _を呼び出すことです。 引数なしでsayを使用することはできません。.sayまたは$ _のいずれかを記述する必要があります。



単純なブロックの代わりに、「尖った」ブロックを使用して、ループ変数の名前を指定できます。



 for 1, 2, 3, 4 -> $i { $i.say }
      
      







「尖った」ブロックは匿名プロシージャに似ており、例外をキャッチしません。 そして、そのようなブロック内にreturnを記述すると、それを呼び出したプロシージャ全体が終了します。 このようなブロックは、複数のパラメーターを取ります。 そして、次のように書くとどうなりますか:



 for 1, 2, 3, 4 -> $i, $j { "$i, $j".say }
      
      







起動時に、以下を受け取ります:



 1 2 3 4
      
      







つまり、リストを調べて、一度に2つの要素をソートします。 これは、任意の数のパラメーターで機能します(少なくとも1つ、およびそれがない場合は$ _が暗黙指定されます)。 さて、私たちが経験しているリストを作成するのはどうですか? もちろん、配列変数を使用できます。



 for @array { .say }
      
      







しかし、単純なケースでは、代わりにmapを使用できます。



 @array.map: *.say;
      
      







または、シーケンスが私たちにとって重要でない場合、ハイパーオペレータ:



 @array».say;
      
      







しかし、私たちは今それについて話していません。 スパン演算子<..>を使用してリストを作成できます。



 for 1..4 { .say }
      
      







多くの場合、0で始まる$ nの数字のリストを作成する必要があります。0.. $ n-1を書くか、ギャップコンストラクター0 .. ^ $ nを使用できますが、Perl 6では^プレフィックスを使用する方法が短くなります。



 for ^4 { .say }
      
      







出力では次のようになります。



 0 1 2 3
      
      







Cスタイルのループを使用する理由は、現在どのリスト項目を使用しているかを知るため、または複数の配列を同時に処理する必要があるためです。 Perl 6には、Z(zip)ステートメントによるこのための短いエントリもあります。



 for @array1 Z @array2 -> $one, $two { ... }
      
      







両方の配列の長さが同じ場合、$ oneは@ array1のすべての要素を通過し、$ 2は@ array2のすべての対応する要素を通過します。 長さが異なる場合、サイクルは停止し、最短の終わりに達します。 したがって、次のようなループに配列インデックスを含めることができます。



 for ^Inf Z @array -> $index, $item { ... }
      
      







無限のリストがあなたの好みに合わない場合:



 for ^@array.elems Z @array -> $index, $item { ... }
      
      







同じ結果になりますが、最もエレガントなオプションは



 for @array.kv -> $index, $item { ... }
      
      







array .kvはキーと値を返し、配列の場合、キーは要素のインデックスにすぎません。



したがって、少なくとも4つの配列を同時に処理できます。



 for @one Z @two Z @three Z @four -> $one, $two, $three, $four { ... }
      
      







パラメーターと.combの制限



静的型が変数の値を制限するように、制約を使用すると、プロシージャとメソッドの操作を制御できます。 多くのPLでは、パラメータをプロシージャに渡し、受信した値を確認する必要があります。 制限があれば、発表時にすぐに検証を行うことができます。 例:偶数は必要ありません。 Perl 5では、次のように書くことができます。



 sub very_odd { my $odd = shift; unless ($odd % 2) { return undef; } #     }
      
      







Perl 6では、よりシンプルにすることができます。



 sub very_odd(Int $odd where {$odd % 2}) { #     }
      
      







偶数のパラメーターでvery_oddを呼び出すと、エラーが発生します。 便宜上、プロシージャをリロードして、任意の番号を操作できます。



 multi sub very_odd(Int $odd where {$odd % 2}) { #     } multi sub very_odd(Int $odd) { return Bool::False; }
      
      







パラメータ制限は、.combメソッドと組み合わせて使用​​すると便利です。 .combとは何ですか? (櫛-櫛)。 髪の毛と櫛の場合、髪の毛を分けて頭に置きます。 .combは.splitの反対です。 最後の方法で、不要な要素で文字列を分割できる場合、.combは必要な要素で文字列を区切ります。 以下に簡単な例を示します。



 say "Perl 6 Advent".comb(/<alpha>/).join('|'); say "Perl 6 Advent".comb(/<alpha>+/).join('|');
      
      







最初の行は、「P | e | r | l | A | d | v | e | n | t」を生成します。各文字を取得して一時配列に入れ、「|」で結合します。 2行目も同様に機能し、可能な限り最大数の文字が連続してキャプチャされ、結果は「Perl | Advent」になります。



ただし、.combははるかに強力です。 ストリングをとかすと、ストランドを操作できます。 ASCII文字列がある場合は、ハイパー演算子を使用して、各部分を同等のASCII文字に置き換えることができます。



 say "5065726C36".comb(/<xdigit>**2/)».fmt("0x%s")».chr #  "P erl 6"
      
      







これは、.mapメソッドを使用して記述できます。



 say "5065726C36".comb(/<xdigit>**2/).map: { chr '0x' ~ $_ } ; #  "P erl 6"
      
      







いつものように、何かをする方法は複数あります。



ただし、タスクはより複雑です。パラメータ、.combおよび.mapの制限を通じて、古代のシーザー暗号を紹介します



 use v6; sub rotate_one( Str $c where { $c.chars == 1 }, Int $n ) { return $c if $c !~~ /<alpha>/; my $out = $c.ord + $n; $out -= 26 if $out > ($c eq $c.uc ?? 'Z'.ord !! 'z'.ord); return $out.chr; } sub rotate(Str $s where {$s.chars}, Int $n = 3) { return ($s.comb.map: { rotate_one( $_, $n % 26 ) }).join( '' ); } die ":\n$*PROGRAM_NAME  _" unless @*ARGS == 2; my Str $mess = @*ARGS[0]; my Int $rotate = @*ARGS[1].Int; say qq|"$mess"  $rotate    "{rotate($mess,$rotate)}".|;
      
      







美しい引数とオプション



Perl 5では、パラメーターの操作は@_を介して構築されます。



  sub sum { [+] @_ } say sum 100, 20, 3; # 123
      
      







[+]-Perl 6からの命令ですが、書く場合



 my $i = 0; $i += $_ for @_; $i
      
      







これはPerl 5でも機能します。Perl6では、Perl 5と同様に、プロシージャに渡されるパラメーターに@_配列を介してアクセスできます。 システムは非常に柔軟で、パラメーターに制限を課しません。 しかし、これは、特に以下を確認する必要がある場合、かなり退屈なプロセスです。

 sub grade_essay { my ($essay, $grade) = @_; die '     Essay' unless $essay ~~ Essay; die '      0  5' unless $grade ~~ Int && $grade ~~ 0..5; %grades{$essay} = $grade; }
      
      







Perl 5では、~~の代わりにisa、%gradesの代わりに$ gradesを書く必要がありましたが、それだけです。 今度は、手動チェックを何回行う必要があるかを見てください。 感じますか? それだけです。



Perl 5では、Sub :: SignaturesやMooseX :: Declareなど、CPANを備えたさまざまな便利なモジュールがこれを行います。



他のメソッドはPerl 6で利用可能です。 たとえば、この例は次のように書くことができます。

 sub grade_essay(Essay $essay, Int $grade where 0..5) { %grades{$essay} = $grade; }
      
      







もう1つ、サードパーティのモジュールはありません。 デフォルト値を設定すると便利な場合があります。



  sub entreat($message = ' !', $times = 1) { say $message for ^$times; }
      
      







これらの値は定数である必要はありませんが、前のパラメーターを含めることもできます。



  sub xml_tag ($tag, $endtag = matching_tag($tag) ) {...}
      
      







デフォルト値が指定されていない場合は、パラメーターをオプションの疑問符としてマークします。



  sub deactivate(PowerPlant $plant, Str $comment?) { $plant.initiate_shutdown_sequence(); say $comment if $comment; }
      
      







特にクールなのは、パラメーターを名前で参照し、任意の順序で渡すことができることです。 パラメーターのシーケンスを思い出せませんでした。



  sub draw_line($x1, $y1, $x2, $y2) { ... } draw_line($x1, $y1, $x2, $y2); # ,     . draw_line($x1, $x2, $y1, $y2); # ! :-/
      
      







そして、名前でそれらを参照できます:



  draw_line(:x1($x1), :y1($y1), :x2($x2), :y2($y2)); #  draw_line(:x1($x1), :x2($x2), :y1($y1), :y2($y2)); #   !
      
      







コロンは「名前付きパラメーターが存在する」ことを意味し、すべて一緒になります:parameter_name($ passed_variable)。 パラメーターと変数の名前が一致する場合、短い表記を使用できます。



  draw_line(:$x1, :$y1, :$x2, :$y2); # ! draw_line(:$x1, :$x2, :$y1, :$y2); #   !
      
      







APIの作成者がすべての人に名前付きパラメーターを使用することを望む場合、関数宣言でコロンを指定する必要があります。



  sub draw_line(:$x1, :$y1, :$x2, :$y2 ) { ... } #   
      
      







名前付きパラメーターはデフォルトではオプションです。 つまり、上の例は次の例と同等です。



  sub draw_line(:$x1?, :$y1?, :$x2?, :$y2?) { ... } #   
      
      







パラメーターを必須にする必要がある場合は、感嘆符を使用します。



  sub draw_line(:$x1!, :$y1!, :$x2!, :$y2!) { ... } #   
      
      







今、それらを転送する必要があります。



可変数のパラメーターはどうですか? 簡単:パラメーターをアスタリスクが前に付いた配列にする:



  sub sum(*@terms) { [+] @terms } say sum 100, 20, 3; # 123
      
      







future関数のパラメーターに制限を設定しない場合、デフォルトの制限* @_を受け取ります。 それが意味するのは、制限の欠如、またはPerl 5の動作のエミュレーションです。



ただし、星のある配列は特定の順序でのみパラメーターを受け取ります。 名前付きパラメーターを渡す必要がある場合は、ハッシュを使用します。



  sub detect_nonfoos(:$foo!, *%nonfoos) { say " 'foo'   ", %nonfoos.keys.fmt("'%s; } detect_nonfoos(:foo(1), :bar(2), :baz(3)); #  'foo'   'bar', 'baz'
      
      







ハッシュの方法で名前付きパラメーターを渡すことができることに注意してください:



  detect_nonfoos(foo => 1, bar => 2, baz => 3); #  'foo'   'bar', 'baz'
      
      







Perl 5との別の違い:デフォルトでは、オプションは読み取り専用です:



  sub increase_by_one($n) { ++$n } my $value = 5; increase_by_one($value); # 
      
      







1つの理由は効率です。 読み取り専用変数のようなオプティマイザー。 2つ目は、プログラマーに正しい習慣を育てることです。 関数型プログラミングは、オプティマイザーと魂の両方に適しています。



一番上の例を機能させるには、次のように記述する必要があります。



  sub increase_by_one($n is rw) { ++$n } my $value = 5; say increase_by_one($value); # 6
      
      







これは機能する場合もありますが、パラメーターのコピーを変更する方が簡単な場合もあります。



  sub format_name($first, $middle is copy, $last) { $middle .= substr(0, 1); "$first $middle. $last" }
      
      







元の変数は変更されません。



Perl 6では、配列またはハッシュを渡すとデフォルトの引数が調整されません。 代わりに、位置合わせを強制するには、「|」を使用します。



  sub list_names($x, $y, $z) { "$x, $y and $z" } my @ducklings = <huey dewey louie>; try { list_names(@ducklings); } say $!; # '  ; #  1,  3' say list_names(|@ducklings); # 'huey, dewey and louie'
      
      







ハッシュが整列されると、その内容は名前付きパラメーターの形式で転送されます。



配列とハッシュに加えて、コードブロックを転送することができます。



  sub traverse_inorder(TreeNode $n, &action) { traverse_inorder($n.left, &action) if $n.left; action($n); traverse_inorder($n.right, &action) if $n.right; }
      
      







3つの文字がタイプリミッターとして機能します。



@配列(位置)

%ハッシュ(連想)

&コード(呼び出される)



$-制限なしのパラメーター。



型を2回割り当てようとするというtrapに陥らないでください-型名と記号を使用して:



  sub f(Array @a) { ... } # ,        sub f( @a) { ... } # ,      sub f(Int @a) { ... } #   
      
      







ここまで読んだら、もう1つのライナーに値します。



  $ perl6 -e '.fmt("%b").trans("01" => " #").say for <734043054508967647390469416144647854399310>.comb(/.**7/)' ### ## ### # # ## # ## # # ### # # ## # #### # #### # # # # # # # # # # # ## # ## ###
      
      







レギュラーストーリー



昔はそれほど遠くない王国で、ティムという名前のPerl 6の学生が簡単な解析の問題に取り組みました。上司であるC氏は、有効な行のみが含まれていることを確認するために、インベントリ情報を含むログについて彼を解析しました。有効な文字列は次のようになります。



  < > <> < > <>
      
      







常連に少し精通している学生は、許容される線を決定するための美しい常連を書きました。コードは次のようになりました。



  next unless $line ~~ / ^^ \d+ \s+ \d+ \s+ \S+ \s+ \N* $$ /
      
      







~~演算子は、左側のスカラーに関して右側の規則性をチェックします。レギュラーシーズンでは、^^は行の始まり、\ d +-少なくとも1桁、\ S +-少なくとも1つの非空白文字、\ N *改行以外の任意の数の文字、\ s +スペース、および$$行末を意味します。 Perl 6では、これらの文字を読みやすくするためにスペースで区切ることができます。そしてすべてが素晴らしかった。

しかし、その後、C氏はログから情報を抽出するだけでなく、それをチェックするのが良いと判断しました。ティムは問題はないと思って、エキサイティングなブラケットを追加しました。だから彼は:



  next unless $line ~~ / ^^ (\d+) \s+ (\d+) \s+ (\S+) \s+ (\N*) $$ /
      
      







試合後、角括弧の各ペアの内容は、エントリ$ / [0]、$ [1]などからアクセスできます。または、変数$ 0、$ 1、$ 2などを通じてティムも幸せでした、Cさん。



しかし、その後、いくつかの線では色が説明から分離されていないことが判明しました。そのような行は次のように見えました。



  <part number> <quantity> <description> (<color>)
      
      







さらに、説明にはスペースを含む任意の数の文字を含めることができます。ハリネズミ、ティムは、タスクが非常に複雑になったと思った!しかし、ティムはアドバイスを求める場所を知っていました。彼はすぐにチャンネル#perl6のirc.freenode.orgに行き、そこで尋ねました。誰かがレギュラーシーズンの各部に名前を付けて、作業をしやすくし、すべての可能な代替品をキャッチするために交互に使用することを勧めました。



最初、ティムはレギュラーシーズンの各部に名前を付けようとしました。Perl 6の常連の説明を見て、ティムはそのようなエントリを作成できることを発見しました。



  next unless $line ~~ / ^^ $<product>=(\d+) \s+ $<quantity>=(\d+) \s+ $<color>=(\S+) \s+ $<description>=(\N*) $$ /
      
      







そして、見つけた後、レギュラーシーズンのピースは、マッチオブジェクトまたは変数$、$、$、$を介して利用できます。それは簡単で、ティムは元気になりました。次に、両方の回線オプションがテストに合格するように、交替を追加しました。



  next unless $line ~~ / ^^ $<product>=(\d+) \s+ $<quantity>=(\d+) \s+ [ | $<description>=(\N*) \s+ '(' $<color>=(\S+) ')' | $<color>=(\S+) \s+ $<description>=(\N*) ] $$ /
      
      







交互をレギュラーシーズンの残りから分離するために、ティムはグループ化された角括弧で彼女を囲みました。これらの括弧は、丸括弧のように正規の部分を分離しますが、分離された部分を$ 0変数などに返しません。ファイル内の括弧をキャッチする必要があるため、TimはPerl 6の別の便利な機能を利用しました。引用符で囲まれたものは、テキストでそのまま検索されます。



ティムは励まされました。彼はC氏にコードを見せてくれて、彼も刺激を受けました! 「よくやった、ティム!」C氏は言った。誰もが幸せで、ティムは誇りを持って輝いた。



しかし、その後、彼は作品を批判的に見ました。一部のラインでは、色は「(color)」または「(color)」または「(color)」に設定されます。結果として、規則性はそのような色を説明に割り当てましたが、$変数をまったく設定しませんでした。それは受け入れられませんでした。 Timはレギュラーシーズンを書き直し、\ s *を追加しました:



  next unless $line ~~ / ^^ $<product>=(\d+) \s+ $<quantity>=(\d+) \s+ [ | $<description>=(\N*) \s+ '(' \s* $<color>=(\S+) \s* ')' | $<color>=(\S+) \s+ $<description>=(\N*) ] $$ /
      
      







それはうまくいきましたが、レギュラーは厄介に見え始めました。そして、Timはチャンネル#perl6に戻りました。



今回、PerlJamという名前のユーザーは次のように述べました。「なぜあなたは普通の文法を文法に入れないのですか?結局のところ、実際にはこれを行っており、各変数に独自の変数を割り当てています。シールド?ティムは考えた。彼は、PerlJamが何について話しているのか分かりませんでした。短い会話の後、ティムは意味を理解したようで、ユーザーに感謝し、コードを書くために座った。今回、規則性は消え、文法に変わりました。彼女の姿は次のとおりです。



 grammar Inventory { regex product { \d+ } regex quantity { \d+ } regex color { \S+ } regex description { \N* } regex TOP { ^^ <product> \s+ <quantity> \s+ [ | <description> \s+ '(' \s* <color> \s* ')' | <color> \s+ <description> ] $$ } } # ...  ,    ,   : next unless Inventory.parse($line);
      
      







まあ、ティムは、今回はすべてがうまく整理されていると思った。



過去の各変数は、文法内で独自の規則性になりました。Perl 6レギュラーでは、名前付きレギュラーが山括弧で囲まれてテストに追加されます。Grammar.parseをスカラーで呼び出す場合、特別なTOPレギュラーが使用されます。動作は同じです-式の見つかった部分は名前付き変数に格納されます。



完璧に制限はありませんが、ティムとミスターCは結果に非常に満足しています。



おわり!



クラス、属性、メソッド、その他



新しいPerl 6オブジェクトモデルでクラスを記述する方法:



 class Dog { has $.name; method bark($times) { say "w00f! " x $times; } }
      
      







classキーワードから始めます。Perl 5を知っている人にとっては、このクラスはパッケージに多少似ていますが、「すぐに使える」という意味の可能性がたくさんあります。



次に、hasキーワードを使用して、アクセサーメソッドを持つ属性を宣言しました。$とnameの間のポイントは、変数にアクセスする機能について報告するピンセットです。Twigil-dotは「属性+アクセサー」を意味します。その他のオプション:



 has $!name; # ,     has $.name is rw; #    
      
      







次に、メソッドはmethodキーワードを介して宣言されます。メソッドは、クラスメソッドテーブルに独自のエントリがあるだけのプロシージャに似ています。$ selfを介して呼び出すことができます。



すべてのクラスは、名前付きパラメーターを属性に割り当てるnewという名前のデフォルトコンストラクターを継承します。クラスのインスタンスを取得するには、次のように記述できます。



 my $fido = Dog.new(name => 'Fido'); say $fido.name; # Fido $fido.bark(3); # w00f! w00f! w00f!
      
      







メソッド呼び出しは、Perl 5では矢印の代わりにドットを使用して行われます。50%短く、他の言語のプログラマーに馴染みがあります。



もちろん、継承があります。これは、子犬クラスを作成する方法です。



 class Puppy is Dog { method bark($times) { say "yap! " x $times; } }
      
      







委任もあります:



 class DogWalker { has $.name; has Dog $.dog handles (dog_name => 'name'); } my $bob = DogWalker.new(name => 'Bob', dog => $fido); say $bob.name; # Bob say $bob.dog_name; # Fido
      
      







ここでは、DogWalkerクラスのdog_nameメソッドの呼び出しがDogクラスのnameメソッドにリダイレクトされることを宣言します。



このすべての美しさの層の下に、メタモデルがあります。クラス、属性、およびメソッドは、メタオブジェクトを通じて表されます。これは、実行時にオブジェクトを操作する方法です。



 for Dog.^methods(:local) -> $meth { say "Dog has a method " ~ $meth.name; }
      
      







。^演算子はオプションです。ただし、メタクラス(クラスを表すオブジェクト)を呼び出します。ここで、クラスで定義されているメソッドのリストを提供するように彼に依頼します(ローカルは他のクラスから継承されたメソッドを除外します)。そして、名前のリストだけでなく、Methodオブジェクトのリストも取得します。この方法でメソッド自体を呼び出すこともできますが、この場合は単に名前を表示します。



Perl 6構文を拡張したいメタプログラミング愛好家は、methodキーワードを使用すると、実際にadd_methodがメタクラスから呼び出されることを知って興奮します。したがって、Perl 6には、オブジェクトを記述するための強力な構文だけでなく、まだ想定していない場合に拡張する機能もあります。



モジュールとエクスポート



Perl 6でライブラリを作成するには、moduleキーワードを使用する必要があります。



 module Fancy::Utilities { sub lolgreet($who) { say "O HAI " ~ uc $who; } }
      
      







これを$ PERL6LIBのどこかにあるFancy / Utilities.pmファイルに配置すると、次のように使用できます。



 use Fancy::Utilities; Fancy::Utilities::lolgreet('Tene');
      
      







特に便利ではありません。Perl 5のように、このモジュールをロードするコードの範囲内でいくつかのものが利用可能であるべきであることを示すことが可能です。これには構文があります。



 # Utilities.pm module Fancy::Utilities { sub lolgreet($who) is export { say "O HAI " ~ uc $who; } } # foo.pl use Fancy::Utilities; lolgreet('Jnthn');
      
      







マークされた「is export」文字はデフォルトでエクスポートされます。文字は名前付きグループの一部としてエクスポートされることにも注意できます。



 module Fancy::Utilities { sub lolgreet($who) is export(:lolcat, :greet) { say "O HAI " ~ uc $who; } sub nicegreet($who) is export(:greet, :DEFAULT) { say "Good morning, $who!"; # Always morning? } sub shortgreet is export(:greet) { say "Hi!"; } sub lolrequest($item) is export(:lolcat) { say "I CAN HAZ A {uc $item}?"; } }
      
      







これらのタグをロードコードで使用して、インポートするものを選択できます。



 use Fancy::Utilities; #   DEFAULTs use Fancy::Utilities :greet, :lolcat; use Fancy::Utilities :ALL; #  ,   
      
      







マルチプロシージャはデフォルトでエクスポートされ、必要に応じてラベルのみを指定できます。



 multi sub greet(Str $who) { say "Good morning, $who!" } multi sub greet() { say "Hi!" } multi sub greet(Lolcat $who) { say "O HAI " ~ $who.name }
      
      







クラスはモジュールの特殊化であるため、クラスから何かをエクスポートすることもできます。また、メソッドをエクスポートして、マルチプロシージャとして使用できます。たとえば、次のコードはIOクラスからcloseメソッドをエクスポートし、「close($ fh);」として呼び出すことができるようにします



 class IO { ... method close() is export { ... } ... }
      
      







Perl 6は、名前によるライブラリからの文字のインポートもサポートしています。



ジャンクション



Perl 6の新機能の中でも、私は何よりも関連が好きです。私はそれらの使用のためのすべてのオプションを表明していませんが、いくつかの便利なトリックを知っています。



ユニオンは、一度に複数の値を含むことができる変数です。奇妙に聞こえますが、例を見てみましょう。値オプションのいずれかに準拠しているかどうか変数を確認する必要があるとします。



 if $var == 3 || $var == 5 || $var == 7 { ... }
      
      







このでたらめを決して愛しませんでした。繰り返しが多すぎます。ユニオンanyを使用すると、次のように記述できます。



 if $var == any(3, 5, 7) { ... }
      
      







この言語の中核にあるのは、「アソシエーションのスレッドへの自動分割」(接合型自動スレッド化)の概念です。これは、ほとんど常に、1つの値のみが期待される場所にユニオンを渡すことができることを意味します。コードは関連付けのすべてのメンバーに対して実行され、結果は取得されたすべての結果の組み合わせになります。



最後の例では、==が各ユニオン要素に対して実行され、$ varと比較されます。各比較の結果は、新しい共用体anyに書き込まれ、ifステートメントのブールコンテキストで評価されます。ブールコンテキストでは、そのメンバーのいずれかがtrueの場合、ユニオンanyはtrueであるため、$ varがユニオン値のいずれかに一致する場合、テストはパスします。



これによりコードを節約でき、かなりきれいに見えます。anyユニオンを記述する別の方法があり、これは|演算子を使用して構築できます。



 if $var == 3|5|7 { ... }
      
      







必要に応じて、noneというユニオンオプションを使用してテスト結果を反転します。



 if $var == none(3, 5, 7) { ... }
      
      







ご想像のとおり、ブールコンテキストのどれもtrueであるのは、その要素がどれもtrueでない場合のみです。



自動スレッド化は、他の場合にも機能します。



 my $j = any(1, 2, 3); my $k = $j + 2;
      
      







どうなるの?最初の例と同様に、$ kの値はany(3、4、5)になります。



関連付けはスマート検索でも機能します。これに適した特別な種類の関連付けがあります。



テキスト文字列があり、セットのすべての正規表現と一致するかどうかを調べる必要があるとします。



 $string ~~ /<first>/ & /<second>/ & /<third>/
      
      







もちろん、最初、2番目、3番目の規則性を定義する必要があります。|、&のように、ユニオンを作成する演算子ですが、この場合、すべてのメンバーがtrueであれば、すべてのユニオンがtrueになります。



アソシエーションの利点は、ライブラリのほとんどすべての関数に転送できることです。この関数は、これらがアソシエーションであることを知る必要はありません(ただし、それらを認識して特別な方法で操作することは可能です)。値と何かのスマートな比較を行う関数がある場合、それを共用体として渡すことができます。



関連の助けを借りてクランクアウトできる便利なものもあります。リスト内の値は次のとおりです。



 any(@list) == $value
      
      







リストは関連付けを簡単かつ自然に機能します。例:



 all(@list) > 0; #      ? all(@a) == any(@b); #     @a   @b?
      
      







合理的な分数



Perl 6は合理的な分数をサポートします。これは単純な方法で作成されます-1つの全体を別の部分に分割することによって。最初は、ここで異常なものを見分けるのは困難です。



 > say (3/7).WHAT Rat() > say 3/7 0.428571428571429
      
      







Ratを文字列に変換するには、数値を小数点付きのレコードとして表示します。しかし、Ratは、Numのような浮動小数点数で満たされている近似表現ではなく、正確な内部表現を使用します。



 > say (3/7).Num + (2/7).Num + (2/7).Num - 1; -1.11022302462516e-16 > say 3/7 + 2/7 + 2/7 - 1 0
      
      







Rat番号の内部で何が起こるかを知る最も簡単な方法は、組み込みの.perlメソッドを使用することです。人間が読める文字列を返します。この文字列は、evalを通じて元のオブジェクトに変わります。



 > say (3/7).perl 3/7
      
      







Ratコンポーネントを選択できます:



 > say (3/7).numerator 3 > say (3/7).denominator 7 > say (3/7).nude.perl [3, 7]
      
      







ラットは、すべての標準的な数値演算で動作します。可能であれば、出力でRatを使用した算術演算もRatを提供し、不可能な場合はNumを提供します。



 > my $a = 1/60000 + 1/60000; say $a.WHAT; say $a; say $a.perl Rat() 3.33333333333333e-05 1/30000 > my $a = 1/60000 + 1/60001; say $a.WHAT; say $a; say $a.perl Num() 3.33330555601851e-05 3.33330555601851e-05 > my $a = cos(1/60000); say $a.WHAT; say $a; say $a.perl Num() 0.999999999861111 0.999999999861111
      
      







Numには、与えられた近似(デフォルトでは1e-6)のRatを与えるファッショナブルな方法があります。



 > say 3.14.Rat.perl 157/50 > say pi.Rat.perl 355/113 > say pi.Rat(1e-10).perl 312689/99532
      
      







仕様によれば、ソースコードに10進数として記述された数値は、Ratとして表されます。



 > say 1.75.WHAT Rat() > say 1.75.perl 7/4 > say 1.752.perl 219/125
      
      







.pick



もう1つのPerl 6の革新は、.pickメソッドです。これにより、ランダムリストアイテムを選択できます。Perl 5では、これは次のように実行できます。



 my @dice = (1, 2, 3, 4, 5, 6); my $index = int (rand() * scalar @dice); print $dice[$index] . "\n"; > 5
      
      







Perl 6ではさらにシンプルになり、さらにいくつかの要素をすぐに選択できます。



 my @dice = 1..6; say @dice.pick(2).join(" "); > 3 4
      
      







さて、10個のd6を投げるとどのような攻撃を受けるのか見てみましょう...

 my @dice = 1..6; say @dice.pick(10).join(" "); > 5 3 1 4 2 6
      
      







.pickはその名前と一致することがわかりました。リストから何かを削除すると、リストには表示されなくなります。このアイテムを再度選択できるようにする必要がある場合は、「replace」という単語を使用します



 my @dice = 1..6; say @dice.pick(10, :replace).join(" "); > 4 1 5 6 4 3 3 5 1 1
      
      







リストには、特定の順序でアイテムを含める必要はありません。「モノポリー」の法案は次のとおりです。



 my @dice = <1 5 10 20 50 100 500>; say @dice.pick(10, :replace).join(" "); > 20 50 100 500 500 10 20 5 50 20
      
      







そして、ここにカードのデッキのオプションがあります:



 use v6; class Card { has $.rank; has $.suit; multi method Str() { return $.rank ~ $.suit; } } my @deck; for <A 2 3 4 5 6 7 8 9 TJQ K> -> $rank { for < > -> $suit { @deck.push(Card.new(:$rank, :$suit)); } } # Shuffle the cards. @deck .= pick(*); say @deck.Str;
      
      







pick(*)は何をしますか?少し後で検討します。とりあえず、カードデッキのコードを改善してデッキクラスを作成する方法を考えてください。



古き良きスイッチ



コンストラクトは「switchステートメント」と呼ばれますが、キーワードは指定されたものに変更されます。



 given $weather { when 'sunny' { say '! ' } when 'cloudy' { say ' . ' } when 'rainy' { say '   ? ' } when 'snowy' { say '! ' } default { say ' ,  .' } }
      
      







ブロックが自動的に処理されるときすべてではないことに注意してください-突然いくつかの条件が同時に満たされると、最初の適切なブロックのみが実行されます。



 given $probability { when 1.00 { say '' } when * > 0.75 { say ' ' } when * > 0.50 { say '' } when * > 0.25 { say '' } when * > 0.00 { say '  ' } when 0.00 { say '  ' } }
      
      







$確率が0.80に等しい場合、コードは「最も可能性が高い」を示し、残りのオプションはそうではありません。複数のブロックを機能させる場合は、単語continueでブロックを終了します(ブロックを制御するbreak / continueキーワードは、成功/続行するように名前が変更されます)。



式がコードで文字列と数値の両方を使用する場合に注意してください。Perl 6は、与えられた値を、これらの物が完全に異なるタイプになる可能性があるときにマッピングする方法をどのように知っていますか?



この場合、いわゆる2つの値が処理されます。前述のスマートな比較。 $ a ~~ $ bとして書かれたスマートな比較は、正規のトリッキーなバージョンです。ギャップが指定されている場合、スマート比較は値がそれに含まれるかどうかを確認します。 $ bがクラス、ロール、またはサブタイプの場合、スマート比較はタイプを比較します。などなど。タイプNumおよびStrの値の場合、それらの等価性がチェックされます。



アスタリスクは、あらゆるものとスマートに比較します。そして、デフォルトはwhen *と同じ意味です。



そして、ここにあなたにとって予期しないものがあります:与えられ、いつ独立して使用できるか。あなたがあなたの「シールド」と言いながら、それがどのようであるかを説明します:



与えられたのは一度限りのサイクルです。



 given $punch-card { .bend; .fold; .mutilate; }
      
      







ここで与えられるのは、単にトピックを設定するもので、$ _として知られています。.methodメソッドの呼び出しは、$ _の呼び出しと同等です。



$ _を明示的にまたは暗黙的に指定するブロック内で使用できるメソッドwhen:



 my $scanning; for $*IN.lines { when /start/ { $scanning = True } when /stop/ { $scanning = False } if $scanning { #  -,    ,   #   ,  'start'  'stop' } }
      
      







指定されたブロックと同じ動作を示すとき、つまり 実行後にブロックに残っているコードをスキップします。上記の例では、これは次の行に進むことを意味します。



$ _を明示的に指定した別の例:



 sub fib(Int $_) { when * < 2 { 1 } default { fib($_ - 1) + fib($_ - 2) } }
      
      







与えられた独立性と時期は、他の状況で使用できます。CATCHブロックを処理するとき、与えられたとき、キャッチされた最後の例外を含む$!変数で動作します。



式が演算子で終わる場合に変更されたレコードシーケンスのオプション:



  say .[0] + .[1] + .[2] given @list; say '  ,    !' when /^ <[]>+ $/;
      
      







に埋め込むことができる場合:



  say '!' when // given $castle;
      
      







与えられたときとコードを非常に明確にするとき、ここに別のperlスタイルの難読化があります



 $ perl6 -e 'for ^20 {my ($a,$b)=<AT CG>.pick.comb.pick(*);\ my ($c,$d)=sort map {6+4*sin($_/2)},$_,$_+4;\ printf "%{$c}s%{$d-$c}s\n",$a,$b}' GC TA CG GC CG GC TA CG CG CG TA TA TA CG TA TA TA AT CG GC
      
      







雪だるまを作る



Mandelbrotセットを例として使用して、Perl 6で複素数を処理する方法を説明します。ここには、高度な数学、美しい写真、あらゆる種類の高度な言語機能があります。



スクリプトの最初のバージョンは次のとおりです。



 use v6; my $height = @*ARGS[0] // 31; my $width = $height; my $max_iterations = 50; my $upper-right = -2 + (5/4)i; my $lower-left = 1/2 - (5/4)i; sub mandel(Complex $c) { my $z = 0i; for ^$max_iterations { $z = $z * $z + $c; return 1 if ($z.abs > 2); } return 0; } sub subdivide($low, $high, $count) { (^$count).map({ $low + ($_ / ($count - 1)) * ($high - $low) }); } say "P1"; say "$width $height"; for subdivide($upper-right.re, $lower-left.re, $height) -> $re { my @line = subdivide($re + ($upper-right.im)i, $re + 0i, ($width + 1) / 2).map({ mandel($_) }); my $middle = @line.pop; (@line, $middle, @line.reverse).join(' ').say; }
      
      







行3〜5は、グラフのピクセルのサイズを指定します。@ * ARGS-コマンドラインパラメーターを含む配列の名前。//演算子は新しい「定義済み」で、定義されている場合は最初の引数を返し、定義されている場合は2番目の引数を返します。言い換えると、3行目は、コマンドラインの最初の引数の値に高さを設定し、そうでない場合は31に設定します。幅は高さに等しく設定されます。$ max_iterationsは、メインループの繰り返し回数を設定します。その後、ポイントがセットに属すると判断されます(対称形状で作業するため、幅は奇数にする必要があります)



行7〜8は、複素平面上の画像の境界を指定します。虚数コンポーネントを数値に追加するのは非常に簡単で、数値または式iに追加するだけです。それは多くのタイプの複合体であることが判明しました。たとえば、RatまたはIntにComplexを追加すると、再びComplexが取得されます。



行10〜17は、マンデルブロの主な機能を定義しています。つまり、反復を続けた場合、方程式z = z * z + c(初期zは0)が有界のままであれば、複素数cはセットに含まれます。そのため、関数が記述されています-$ max_iterations回実行するループを定義します。モジュールzが2より大きくなると、境界が維持されないことがわかっているため、チェック$ z.abs> 2を使用します。これが発生した場合、ループを終了し、ポイントが黒であることを示す1を返します。ループが最大回数を超えても値が超えない場合、0を返し、ポイントは白になります。



19行目から21行目は、要素数がcountである$ lowから$ highへの算術的進行を返す補助関数です。タイプ$ lowおよび$ highは指定されていないため、基本的な算術演算を許可するすべてのタイプがここで機能します。このスクリプトでは、これは最初にNum、次にComplexです。



行23〜24は、PBMファイルのヘッダーを指定します。



26〜30行目は絵を描きます。 $ upper-right.reは複素数$の実数部であり、$ upper-right.imは虚数です。サイクルはギャップの実際の部分を通過します。ループでは、虚数部のサブセットを再度使用して、画像のこの行の半分をチェックする必要がある複雑な値のリストを作成します。次に、このリストはmapを使用してマンデル関数を介して実行され、出力は中点を含む半分のシリーズのゼロと1のリストです。



これは、マンデルブロ集合が軸に関して対称であるためです。したがって、最後の中間点を選択し、リストを展開して、同じリストになり、逆方向にのみ(中間点を除く)になるようにします。ローン。これを結合に渡し、行を最後まで埋めて印刷します。



このような操作により、通常の位置から90度回転したセットが生成されるため、この種の美しい雪だるまが得られ



画像



ます。このアルゴリズムは、ハイパー演算子を介して自動的に並列化できますが、1つの問題があります:ハイパー演算子は通常の手順を呼び出すことができません。クラスのメソッドと演算子のみを呼び出します。したがって、Complexクラスを調整して、.mandelメソッドが含まれるようにします。



 augment class Complex { method mandel() { my $z = 0i; for ^$max_iterations { $z = $z * $z + self; return 1 if ($z.abs > 2); } return 0; } } for subdivide($upper-right.re, $lower-left.re, $height) -> $re { my @line = subdivide($re + ($upper-right.im)i, $re + 0i, ($width + 1) / 2)>>.mandel; my $middle = @line.pop; (@line, $middle, @line.reverse).join(' ').say; }
      
      







違いは、マンデルがメソッドになり、自己が$ c引数の役割を果たしたことです。そして、マップ({mandel($ _)})の代わりに、ハイパー演算子を使用します。



しかし、誰かがComplexクラスを変更したくない場合は、マンデルを演算子に変えることができます。



 sub postfix:<>(Complex $c) { my $z = 0i; for ^$max_iterations { $z = $z * $z + $c; return 1 if ($z.abs > 2); } return 0; } for subdivide($upper-right.re, $lower-left.re, $height) -> $re { my @line = subdivide($re + ($upper-right.im)i, $re + 0i, ($width + 1) / 2)>>; my $middle = @line.pop; (@line, $middle, @line.reverse).join(' ').say; }
      
      







Perl 6はUnicodeをサポートしているので、楽しみを持って、雪だるまキャラクターを介してオペレーターを設定できます。



楽堂の最新バージョンでは、スクリプトを再編集して、カラフルな画像を作成しました。それは遅く、多くのメモリを消費しますが、安定して動作します。1001×1001の解像度のMandelbrotがたくさんあります。これは14時間誤算され、6.4 GBのメモリを必要としました。



画像



役割



OOPの伝統によれば、クラスはインスタンスの管理と再利用を扱います。残念ながら、これは逆の結果につながります。再利用はクラスを小さく最小限にする傾向がありますが、複雑なエンティティを表す場合は、これに必要なすべてをサポートする必要があります。Perl 6では、クラスはインスタンスの制御を保持し、ロールは再利用します。



ロールとは何ですか?それぞれが異なるタイプの製品を表すクラスの束を構築していると想像してください。一部には共通の機能と属性があります。たとえば、BatteryPowerの役割がある場合があります。



 role BatteryPower { has $.battery-type; has $.batteries-included; method find-power-accessories() { return ProductSearch::find($.battery-type); } }
      
      







一見、属性とメソッドのクラスのように見えます。ただし、ロールを自分で使用することはできません。代わりに、doesキーワードを使用してクラスに挿入(構成)します。



 class ElectricCar does BatteryPower { has $.manufacturer; has $.model; }
      
      







構成は、ロールから属性とメソッドを取得し、それらをクラスにコピーします。これ以降、すべてがクラス自体で属性とメソッドが定義されているかのように機能します。メソッドが配布されるときに親クラスが検索される継承とは異なり、クラスは特定の役割を果たしているかどうかの質問に応じてyesと言うことを除いて、プログラムの実行時に接続を持ちません。



興味は、クラスにいくつかのロールを挿入することから始まります。SocketPowerという別の役割があるとします。



 role SocketPower { has $.adapter-type; has $.min-voltage; has $.max-voltage; method find-power-accessories() { return ProductSearch::find($.adapter-type); } }
      
      







ラップトップはコンセントまたはバッテリーで実行できるため、両方の役割を挿入します。



 class Laptop does BatteryPower does SocketPower { }
      
      







コンパイルを試みますが、何も機能しません。ミックスインや継承とは異なり、すべての役割は同じ位置にあります。両方の役割が同じ名前のメソッド(この場合はfind-power-accessories)を提供する場合、競合が発生します。これは、実行する必要があることを決定するメソッドをクラスに提供することで解決できます。



 class Laptop does BatteryPower does SocketPower { method find-power-accessories() { my $ss = $.adapter-type ~ ' OR ' ~ $.battery-type; return ProductSearch::find($ss); } }
      
      







これは最も一般的な役割の使用例ですが、唯一の例ではありません。ロールは受け入れられ、dosおよびbutステートメントを介してオブジェクトに(つまり、クラスレベルで、ただしオブジェクトレベルで)挿入できます。これは、JavaおよびC#のインターフェースと同様に機能します。しかし、今はそれについて話しましょう。Perl6のロールが一般化されたプログラミング、またはパラメトリック多相性をどのように扱うかをよりよく示します。



ロールはパラメーターを受け取ることができます。パラメーターはタイプまたは値です。たとえば、送料を計算する必要がある製品に割り当てる役割を作成できます。ただし、配送費用を計算するための他のモデルを提供できる必要があるため、配送費用をロールのパラメーターとして処理できるクラスを使用します。



 role DeliveryCalculation[::Calculator] { has $.mass; has $.dimensions; method calculate($destination) { my $calc = Calculator.new( :$!mass, :$!dimensions ); return $calc.delivery-to($destination); } }
      
      







ここ::ロール名の後の角かっこ内の電卓は、オブジェクトをキャプチャし、ロール内の電卓名と関連付けることを示します。次に、このオブジェクトを使用して.newを呼び出すことができます。 ByDimensionとByMassの送料を計算するクラスを作成したとします。次に、次のように記述できます。



 class Furniture does DeliveryCalculation[ByDimension] { } class HeavyWater does DeliveryCalculation[ByMass] { }
      
      







パラメーターを使用してロールを定義する場合は、単に角かっこでパラメーターのセットを指定し、ロールを使用する場合は、引数のリストを角かっこで囲みます。したがって、この場合、Perl 6パラメーターセットの全機能を使用できます。また、デフォルトのロールは複数、複数であるため、同じ名前の多数のロールを指定できます。これらのロールは、異なるタイプと異なるタイプのパラメーターを取ります。



角括弧を使用してロールをパラメーター化する機能に加えて、各ロールがパラメーターを1つだけ受け入れる場合は、ofキーワードを使用することもできます。したがって、次の宣言の後:



 role Cup[::Contents] { } role Glass[::Contents] { } class EggNog { } class MulledWine { }
      
      







このように書くことができます:



 my Cup of EggNog $mug = get_eggnog(); my Glass of MulledWine $glass = get_wine();
      
      







次のように要約することもできます。



 role Tray[::ItemType] { } my Tray of Glass of MulledWine $valuable;
      
      







最後の例は、Tray [Glass [MulledWine]]のより読みやすいエントリです。



Anything Anything



は、特定のコンテキストで意味のあるすべてを表すPerl 6の型です。



例:



 1..* #   my @x = <abcd e>; say @x[*-2] #     #  'd' say @x.map: * ~ 'A'; #  A  ,   #   say @x.pick(*) #     @x #    
      
      







それでは、この魔法はどのように機能しますか?



いくつかの例は単純です。*式のメンバーの位置で、Whateverオブジェクトが返され、一部の組み込み関数(たとえば、List.pick)はそれをどう処理するかを知っています。ところで、Perl 6はファイルを予測的に解析します。つまり、コンパイラーがコードを読み取ると、式のメンバーまたは演算子のいずれに会ったかを常に認識します。



 say 2 + 4 | | | | | | | +   () | | +  ( +) | +   () +   (listop),      
      
      







したがって、記録に



 * * 2
      
      







最初の*は式のメンバーとして扱われ、2番目は演算子として扱われます。この例では、コードブロック-> $ x {$ x * 2}を生成します。



 my $x = * * 2; say $x(4); # says 8
      
      







同じ方法



 say @x.map: * ~ 'A';
      
      







の短いエントリです



 say @x.map: -> $x { $x ~ 'A' };
      
      







でも



 say @x.map: *.succ;
      
      







の短いエントリ



 say @x.map: -> $x { $x.succ };
      
      







並べ替えにも便利なものは何でも-たとえば、数字の順序でリストを並べ替えるには(プレフィックス+は数値に変換することを意味します):



 @list.sort: +*
      
      







文字列の規則に従ってリストをソートするには(プレフィックス〜は、値を文字列形式に変換することを意味します):



 @list.sort: ~*
      
      







ちょっとしたトリック



Perl 6のシンプルで強力なアイデアの1つは、内省です。YaPの場合、これは言語自体を使用して言語について質問できるメカニズムです。たとえば、オブジェクトインスタンスには、どのクラスに属するかを示すメソッド、使用可能なメソッドをリストするメソッドなどがあります。



プロシージャにも、このプロシージャの名前を報告するメソッドがあります。



  sub foo (Int $i, @stuff, $blah = 5) { ... } say &foo.name; #  "foo"
      
      







これはあまり意味がありませんが、プロシージャをスカラーに割り当てたり、エイリアスを付与したり、オンザフライで作成したりできるため、コードを見たときにプロシージャ名が必ずしも明確ではないことに注意してください。



  my $bar = &foo; # ...    ... say $bar.name; #  - , ?
      
      







手順を学習するためのいくつかの方法を次に示します。



  say &foo.signature.perl; #    ? say &foo.count; #    ? say &foo.arity; #     ?
      
      







最後のパラメーターはアリティ、または必須パラメーターの数です。Perl 6のイントロスペクションのおかげで、以前は不可能だったことができます。たとえば、Perl 5では、mapブロックはポイントのリストを一度に1つずつ取得し、それを1つ以上の新しいポイントに変換して、そこから新しいリストを作成します。Perl 6は予想される引数の数を知っているので、必要なだけ多くを取ることができます。



  my @foo = map -> $x, $y { ... }, @bar; #      @bar   @foo my @coords = map -> $x, $y, $z { ... }, @numbers; #   @numbers    
      
      







別の利点は、文字列比較以外の基準で配列をソートするためのより便利なメカニズムです。配列の並べ替え手順を指定する場合、通常は2つの引数を取ります—配列から比較する項目です。カルマで人々をソートしたい場合、次のように書きます。



 #!/usr/bin/perl6 use v6; class Person { has $.name; has $.karma; method Str { return "$.name ($.karma)" } #    } my @names = <Jonathan Larry Scott Patrick Carl Moritz Will Stephen>; my @people = map { Person.new(name => $_, karma => (rand * 20).Int) }, @names; .say for @people.sort: { $^a.karma <=> $^b.karma };
      
      







しかし。内観のおかげで、別のオプションがあります。1つのパラメーターのみをとるプロシージャーを渡すことにより、Perl 6は、シュワルツ変換に相当するものを自動的に作成できます。

en.wikipedia.org/wiki/Swartz Transformation



  .say for @people.sort: { $^a.karma };
      
      







ただし、パラメータは1つしかないため、プロシージャ内で$ _が暗黙的に設定されるため、余分な文字を削除できます。



  .say for @people.sort: { .karma };
      
      







この例では、配列の各要素に対して.karmaメソッドを1回(通常の場合の比較のために2回ではなく)呼び出し、これらの結果に従って配列を並べ替えます。



別のトリックは、組み込み型システムです。上記の例では、perlは数値を使用していると推測するため、数値の並べ替えの必要性を宣言しませんでした。ソートのタイプを強制する必要がある場合、+または〜を使用します。



  .say for @people.sort: { +.karma }; #  .say for @people.sort: { ~.karma }; # 
      
      







.minおよび.maxメソッドでは、これは特に便利です。また、ソート基準を決定する手順も採用しています。



  say @people.min: { +.karma } #  say @people.max: { ~.name } # 
      
      







これはWhateverを使用して書くこともできます:



  .say for @people.sort: *.karma; say @values.min: +*.karma; say @values.max: ~*.name;
      
      







文法とアクション



解析する必要のあるテキストがたくさんあるとしましょう。Perlはこれを目的としていますか?問題を特定します。次のテキストで質問と回答を説明します。



 pickmany:     ? ac:  ac:  ac:  ai:  pickone:     ? ac:  ai:  ai:  ai: 
      
      







Perl 6では、構文解析用の文法を定義します。これは、正規表現を含む特別な種類の名前空間です。また、いくつかの名前付き式を定義して、解析タスクを部分に分割します。



 grammar Question::Grammar { token TOP { \n* <question>+ } token question { <header> <answer>+ } token header { ^^ $<type>=['pickone'|'pickmany'] ':' \s+ $<text>=[\N*] \n } token answer { ^^ \s+ $<correct>=['ac'|'ai'] ':' \s+ $<text>=[\N*] \n } }
      
      







デフォルトでは、文法では空白は無視され、行全体で一致が検索されます-修飾子/ xおよび/ sがPerl 5に含まれているかのように。TOPは文法全体で一致を探す場合に呼び出される規則性です。



「トークン」は、「正規表現」、「トークン」、「ルール」など、規則性を指定するために使用される3つの識別子の1つです。

「正規表現」は単純なバージョンであり、他の2つは

「トークン」が返品を禁止するオプションを追加し、「ルール」が返品を禁止し、レギュラーシーズンで指定されたスペースのリテラル検索を含みます。 「ルール」は使用しません。



この構文は、別の名前付きレギュラーを呼び出すために使用されます。「^^」はすべてのテキストの始まりを示す「^」とは対照的に、行の始まりを示すために使用されます。角括弧は、Perl 5では文字列の類似部分(?:)の配列に影響を与えないグループです。



=記号は、左側の右側に名前を割り当てます。この文法を探して検索結果を表示するとどうなるか見てみましょう。



 my $text = Q { pickmany:     ? ac:  ac:  ac:  ai:  pickone:     ? ac:  ai:  ai:  ai:  }; my $match = Question::Grammar.parse($text); say $match.perl;
      
      







ここでは、232行の出力全体を含めません。1つの部分、質問について考えてみましょう。



 #   for $match<question>.flat -> $q { say $q<header><text>; }
      
      







$ matchはスカラーコンテナに含まれる配列であるため、.flatが使用されます。山括弧は、次の表記と同等です。



 #   for $match{'question'}.flat -> $q { say $q{'header'}{'text'}; }
      
      







これは、オブジェクトに名前付きアイテムがハッシュ値として含まれており、繰り返しが配列に含まれていることを示しています。Perl 5()のように、括弧で作成された結果の配列が見つかった場合、角括弧を使用して位置配列を介してその要素に到達できます(配列を操作する場合など)。



次のステップは、いくつかのクラスを作成し、オブジェクトに基づいてそれらを伝播することです。クラス定義:



 class Question::Answer { has $.text is rw; has Bool $.correct is rw; } class Question { has $.text is rw; has $.type is rw; has Question::Answer @.answers is rw; }
      
      







検索結果から質問オブジェクトを作成することはそれほど難しくありませんが、見苦しいです:



 my @questions = $match<question>.map: { Question.new( text => ~$_<header><text>, type => ~$_<header><type>, answers => $_<answer>.map: { Question::Answer.new( text => ~$_<text>, correct => ~$_<correct> eq 'ac', ) }, ); };
      
      







レギュラーシーズンで繰り返されると、オブジェクトに配列が表示されることに留意して、属性ごとにmapを実行し、それぞれに対してQuestionオブジェクトを作成します。各エントリはfromの配列をドラッグし、mapを使用してQuestion :: Answerオブジェクトのリストを作成します。見つかった値をMathオブジェクトから文字列に変換します。



このアプローチはスケーリングしません。その場でオブジェクトを作成する方が便利です。これを行うには、オブジェクトを引数として渡します:アクションを文法の.parse()メソッドに渡します。解析エンジンは、処理された通常の名前と同じ名前のメソッドを呼び出し、Matchオブジェクトが引数として渡されます。メソッドが実行時に「make()」を呼び出す場合、「make()」の引数は.ast属性として書き込まれます(「抽象構文ツリー」、Matchオブジェクトの抽象構文ツリー)。



しかし、これはかなり抽象的なものです。コードを見てみましょう。3つのレギュラーと同じ名前のメソッドを持つクラスが必要です。



 class Question::Actions { method TOP($/) { make $<question>».ast; } method question($/) { make Question.new( text => ~$<header><text>, type => ~$<header><type>, answers => $<answer>».ast, ); } method answer($/) { make Question::Answer.new( correct => ~$<correct> eq 'ac', text => ~$<text>, ); } }
      
      







$ /はMatchオブジェクトの伝統的な名前で、$ _と同じように特別です-属性にアクセスするための特別な構文があります。変数($および$ [1])なしの名前または位置を介したアクセスは、$ /($ /および$ / [1])へのアクセスに変換されます。違いは1文字ですが、視覚的なノイズを避け、Perl 5の$ 1、$ 2、$ 3に似たセマンティック構造を使用できます



。TOPメソッドでは、メソッドのハイパーメソッド呼び出しを使用して、$の各アイテムの.ast属性のリストを作成します。アクションメソッドでmakeを呼び出すところはどこでも、返されるMatchオブジェクトの.ast属性として何かを定義するため、これは質問メソッドで行うものへの呼び出しにすぎません。



「question」メソッドでは、新しいQuestionオブジェクトを作成し、一致オブジェクトからすべての属性を渡し、通常の「answer」通常の質問「question」の各呼び出しで取得したオブジェクトのリストに「answer」属性を割り当てます。



「answer」メソッドでは、属性の「Bool」タイプに合うように「正しい」属性に比較結果を割り当てることで同じことを行います。



解析するとき、この新しいクラスのインスタンスを作成し、オブジェクトをパラメーターとして渡します:actionをgrammar .parseメソッドに渡してから、返される検索オブジェクトの.ast属性から構築されたオブジェクトを取得します。



私の$アクション=質問:: Actions.new();

私の質問 =質問:: Grammar.parse($テキスト、:アクション($アクション))。ast.flat;



これで、作成したオブジェクトをチェックして、すべてが計画どおりに進んでいることを確認できます。



 for @questions -> $q { say $q.text; for $q.answers.kv -> $i, $a { say " $i) {$a.text}"; } }
      
      







完全を期すために、質問を行い、回答を取得して評価するメソッドをQuestionに追加しましょう。



質問、回答、および入力要求の提示から始めましょう。



  method ask { my %hints = ( pickmany => "   ,   ", pickone => "   ", ); say "\n{%hints{$.type}}\n"; say $.text; for @.answers.kv -> $i, $a { say "$i) {$a.text}"; } print "> ";
      
      







STDINから行を取得し、そこから数値を抽出します。



  my $line = $*IN.get(); my @answers = $line.comb(/<digit>+/)>>.Int.sort
      
      







「くし」は「分割」の反対です。つまり、捨てる必要があるものではなく、残す必要があるものを決定するという意味です。利点は、分離文字を選択する必要がないことです。ユーザーは「1 2 3」、「1、2、3」、または「1、2、3」と入力することもできます。次に、hyperoperatorメソッドを呼び出して、見つかった文字の配列から整数の配列を作成し、並べ替えます。



次に、すべての正解のインデックスの配列を作成し、正解を見つけましょう。



  my @correct = @.answers.kv.map({ $^value.correct ?? $^key !! () }); if @correct ~~ @answers { say ",  !"; return 1; } else { say " ,  "; return 0; } }
      
      







それぞれの質問に対してそれを呼び出し、新しいメソッドのマップを通して結果を収集します。



 my @results = @questions.map(*.ask); say "\nFinal score: " ~ [+] @results;
      
      







結果は次のようになります。



 [sweeks@kupo ~]$ perl6 /tmp/questions.pl    ,        ? 0)  1)  2)  3)  > 0 1 2 ,  !         ? 0)  1)  2)  3)  > 1  ,    : 1
      
      







プログラムの全文は次のとおりです。



 class Question::Answer { has $.text is rw; has Bool $.correct is rw; } class Question { has $.text is rw; has $.type is rw; has Question::Answer @.answers is rw; method ask { my %hints = ( pickmany => "    ,   ", pickone => "    ", ); say "\n{%hints{$.type}}\n"; say $.text; for @.answers.kv -> $i, $a { say "$i) {$a.text}"; } print "> "; my $line = $*IN.get(); my @answers = $line.comb(/<digit>+/)>>.Int.sort @correct = @.answers.kv.map({ $^value.correct ?? $^key !! () }); if @correct ~~ @answers { say " ,  !"; return 1; } else { say "  ,  "; return 0; } } } grammar Question::Grammar { token TOP { \n* <question>+ } token question { <header> <answer>+ } token header { ^^ $<type>=['pickone'|'pickmany'] ':' \s+ $<text>=[\N*] \n } token answer { ^^ \s+ $<correct>=['ac'|'ai'] ':' \s+ $<text>=[\N*] \n } } class Question::Actions { method TOP($/) { make $<question>».ast; } method question($/) { make Question.new( text => ~$<header><text>, type => ~$<header><type>, answers => $<answer>».ast, ); } method answer($/) { make Question::Answer.new( correct => ~$<correct> eq 'ac', text => ~$<text>, ); } } my $text = Q { pickmany:     ? ac:  ac:  ac:  ai:  pickone:     ? ac:  ai:  ai:  ai:  }; my $actions = Question::Actions.new(); my @questions = Question::Grammar.parse($text, :actions($actions)).ast.flat; my @results = @questions.map(*.ask); say "\n : " ~ [+] @results;
      
      







オペレーターの過負荷



Perl 6では、既存の演算子をオーバーロードし、新しい演算子を定義できます。演算子は、特別な名前を持つ単純なマルチプロシージャであり、マルチプロシージャの標準ルールを使用して、目的の演算子バリアントを決定します。



一般的な例は、数学的表記に似た階乗演算子の定義です。



 multi sub postfix:<!>(Int $n) { [*] 1..$n; } say 3!;
      
      







定義の最初の部分は、構文カテゴリ(プレフィックス、ポストフィックス、インフィックス、サーカムフィックス、またはポストサーキュフィックス)です。コロンの後には、演算子が記述されている山括弧があります。サーカムフィックスの場合、2組のブラケットが必要ですが、他のすべてのブラケットには1つで十分で、その中に複数の文字が含まれる場合があります。例では、後置演算子を定義しました!整数の操作。



tighter、equiv、looseなどの追加属性を設定して、他の演算子よりも優先順位を指定できます。



既存の演算子の置換を指定すると、新しい定義はそのマルチプロシージャのセットに単純に追加されます。たとえば、クラスを定義し、+演算子を使用してそのオブジェクトを追加できることを決定できます。



 class PieceOfString { has Int $.length; } multi sub infix:<+>(PieceOfString $lhs, PieceOfString $rhs) { PieceOfString.new(:length($lhs.length + $rhs.length)); }
      
      







もちろん、実際の例はより複雑で、いくつかの変数が含まれています。文字列の等価性チェックを指定できます。



 multi sub infix:<==>(PieceOfString $lhs, PieceOfString $rhs --> Bool) { $lhs.length == $rhs.length; }
      
      







避けるべきことは、プレフィックス演算子のオーバーロード:<〜>(文字列への変換)です。これを行うと、すべての文字列変換を傍受することができなくなります。代わりに、ジョブを実行するStrメソッドをクラスに定義するのが最善です。



 use MONKEY_TYPING; augment class PieceOfString { method Str { '-' x $.length; } }
      
      







このメソッドは、従来の〜演算子によって呼び出されます。名前が型と一致するメソッドは型の変換に使用されるため、これが理にかなっている場合は、StrメソッドとNumメソッドをクラスに設定できます。



また、Perl 6のソースコードはUnicodeで記述されているため、豊富な文字を使用して新しい演算子を定義できます。



エデンの園の怠fruitsな果物



他の言語ではあまり見られないデザインを考えてみましょう-ギャザーと呼ばれる反復子コンストラクターです。



歴史的に、多くのオオムギ植物は便利なマップ、grep、およびソート機能を知っています。



 my @squares = map { $_ * $_ }, @numbers; my @primes = grep { is-prime($_) }, @numbers;
      
      







mapとgrepは、それらを連鎖させる方法を学ぶときに特に役立ちます。



 my @children-of-single-moms = map { .children }, grep { !.is-married }, grep { .gender == FEMALE }, @citizens;
      
      







これにより、その場合のキャッシングのイディオムであるシュワルツ変換が作成されました。ソートがリソースを集中的に使用する場合:



 my @files-by-modification-date = map { .[0] }, #  sort { $^a[1] <=> $^b[1] }, map { [$_, $_ ~~ :M] }, #    @files;
      
      







前の例の1つは、この変換がどのようにソートに組み込まれるかを示しました。



 my @files-by-modification-date = sort { $_ ~~ :M }, @files;
      
      







では、ギャザーはどうですか?これは、mapとgrepのこのような一般化です。



 sub mymap(&transform, @list) { gather for @list { take transform($_); } }; sub mygrep(&condition, @list) { gather for @list { take $_ if condition($_); } };
      
      







次のブロック内にリストを作成しているというシグナルを収集します。各テイクに要素が追加されます。これは、匿名配列にプッシュする方法です。



 my @result = gather { take $_ for 5..7 }; #  - my @result; push @result, $_ for 5..7; #  
      
      







Gatherの最初のプロパティは、map、grep、sortが十分ではないときにリストを作成するときに使用されることがわかります。もちろん、それらを再発明しないでください...しかし、これが可能であるという事実は、独自の特別な要件を設定しながら、よさそうです。



 sub incremental-concat(@list) { my $string-accumulator = ""; gather for @list { take ~($string-accumulator ~= $_); } }; say incremental-concat(<ab c>).perl; # ["a", "ab", "abc"]
      
      







繰り返しの間に$ string-accumulatorを処理する必要があるため、例はmapより便利です。



Gatherの2番目のプロパティ-take呼び出しはGatherブロックのスコープで発生する必要がありますが、レキシカル領域にある必要はありません-ダイナミックにのみです。違いを理解していない人のために、私は説明します:



 sub traverse-tree-inorder(Tree $t) { traverse-tree-inorder($t.left) if $t.left; take transform($t); traverse-tree-inorder($t.right) if $t.right; } my $tree = ...; my @all-nodes = gather traverse-tree-inorder($tree);
      
      







ここでは、&traverse-tree-inorder呼び出しをGatherステートメントでラップします。命令自体には取得するレキシカルコールは含まれていませんが、呼び出されたプロシージャには含まれているため、内部で取得することは、gatherのコンテキスト内にあることを覚えています。これは動的なコンテキストです。



Gatherの3番目のプロパティは、「遅延」です。ツリートラバーサルコードの場合、これは、@ all-nodes割り当てが実行されたとき、ツリーがまだバイパスされていないことを意味します。クロールは、配列の最初の要素@ all-nodes [0]にアクセスしたときにのみ開始されます。そして、左端の頂点が見つかると停止します。 @ all-nodes [1]をリクエスト-ツアーは、2番目の頂点を見つけた後に停止するために、終了した場所から再開します。



つまり、収集ブロック内のコードは、要求された以上の作業を行わないように開始および停止します。これが「遅延」動作です。



実際、これは遅延実行です。Perl 6はGatherブロック内でコードを実行することを約束しますが、その情報が必要な場合のみです。興味深いことに、ほとんどすべての配列はデフォルトで遅延しており、ファイルからも行を読み取ります。mapとgrepは、gatherを使用して作成できるだけでなく、それら自体も怠zyです。この動作により、スレッドのプログラミングや無限配列の使用が可能になります。



 my @natural-numbers = 0 .. Inf; my @even-numbers = 0, 2 ... *; #   my @odd-numbers = 1, 3 ... *; my @powers-of-two = 1, 2, 4 ... *; #   my @squares-of-odd-numbers = map { $_ * $_ }, @odd-numbers; sub enumerate-positive-rationals() { #     take 1; for 1..Inf -> $total { for 1..^$total Z reverse(1..^$total) -> $numerator, $denominator { take $numerator / $denominator; } } } sub enumerate-all-rationals() { map { $_, -$_ }, enumerate-positive-rationals(); } sub fibonacci() { gather { take 0; my ($last, $this) = 0, 1; loop { # infinitely! take $this; ($last, $this) = $this, $last + $this; } } } say fibonacci[10]; # 55 #   ,     sub merge(@a, @b) { !@a && !@b ?? () !! !@a ?? @b !! !@b ?? @a !! (@a[0] < @b[0] ?? @a.shift !! @b.shift, merge(@a, @b)) } sub hamming-sequence() # 2**a * 3**b * 5**c, where { all(a,b,c) >= 0 } gather { take 1; take $_ for merge( (map { 2 * $_ } hamming-sequence()), merge( (map { 3 * $_ }, hamming-sequence()), (map { 5 * $_ }, hamming-sequence()) )); } }
      
      







最後の手順は、Perl 6のハミング問題を解決する



ことです。次は、クリスマスツリーを突然描画するセルオートマトンを実装する次の「意味不明」です。



 $ perl6 -e 'my %r=[^8]>>.fmt("%03b") Z (0,1,1,1,1,0,0,0);\ say <. X>[my@i=0 xx 9,1,0 xx 9];\ for ^9 {say <. X>[@i=map {%r{@i[($_-1)%19,$_,($_+1)%19].join}},^19]};' .........X......... ........XXX........ .......XX..X....... ......XX.XXXX...... .....XX..X...X..... ....XX.XXXX.XXX.... ...XX..X....X..X... ..XX.XXXX..XXXXXX.. .XX..X...XXX.....X. XX.XXXX.XX..X...XXX
      
      







Perl 6標準文法



文法を言語の重要な構成要素と呼ぶのは奇妙です。明らかに、構文は非常に重要です-しかし、定義した後は、文法を使用して構文を記述し、パーサーを構築するだけですよね?



Perl 6ではなく、構文は動的です。新しいキーワードや元のデザインではカバーされていないものを識別するのに役立ちます。より正確には、Perl 6は言語構文を変更するモジュールとアプリケーションをサポートします。この言語は、新しい演算子の定義に加えて、マクロ、命令の種類、文字などの動的な追加をサポートしています。



文法の重要な特徴は、正規表現の使用です。PRVでは、複数のレギュラーを1つのカテゴリに組み合わせることができます。より伝統的な文法では、次のように書きます。



  rule statement { | <if_statement> | <while_statement> | <for_statement> | <expr> } rule if_statement { 'if' <expr> <statement> } rule while_statement { 'while' <expr> <statement> } rule for_statement { 'for' '(' <expr> ';' <expr> ';' <expr> ')' <stmt> }
      
      







PRVでは、次のように記述します。



  proto token statement { <...> } rule statement:sym<if> { 'if' <expr> <statement> } rule statement:sym<while> { 'while' <expr> <statement> } rule statement:sym<for> { 'for' '(' <expr> ';' <expr> ';' <expr> ')' <stmt> } rule statement:sym<expr> { <expr> }
      
      







説明した構造のいずれかで一致を探していると判断しますが、PRVバージョンの拡張ははるかに簡単です。最初のバージョンでは、新しいステートメント(「repeat..until」など)を追加するには、「ru​​le statement」宣言全体を書き直す必要があります。しかし、PRVでは、もう1つのルールを追加するだけです。



  rule statement:sym<repeat> { 'repeat' <stmt> 'until' <expr> }
      
      







このルールは、PDPの候補に追加されます。そして、これは新しい文法バージョンで動作します:



  grammar MyNewGrammar is BaseGrammar { rule statement:sym<repeat> { 'repeat' <stmt> 'until' <expr> } }
      
      







MyGrammarは、baseGrammarと同じ方法で、repeat..until演算子の追加定義を使用して解析します。



標準文法のもう1つの有用なコンポーネントは、高度なエラー診断です。「ここでエラー」のようなメッセージの代わりに、言語はそれを解決するためのオプションを提供します。さらに、Perl 5からPerl 6に移行する際に、変更された値の構成をトレースするのを支援する努力がなされました。たとえば、unlessブロックでelseを使用しようとすると、エラーが発生します。



  unless does not take "else" in Perl 6; please rewrite using "if"
      
      







または、三項演算子(?:)を使用すると、パーサーは以下を返します。



  Unsupported use of "?:"; in Perl 6 please use "??!!"
      
      







Rakudoコンパイラに適用される新しいPerl 6言語に関するメモをお楽しみください。



All Articles