输入数据(存储在一个名为的文件中scores.txt)如下所示:

    第一行是球员名单。每个后续行记录匹配的结果。

    这是在Raku中解决该问题的一种方法:

    1. use v6;
    2. my $file = open 'scores.txt';
    3. my @names = $file.get.words;
    4. my %matches;
    5. my %sets;
    6. for $file.lines -> $line {
    7. next unless $line; # ignore any empty lines
    8. my ($pairing, $result) = $line.split(' | ');
    9. my ($p1, $p2) = $pairing.words;
    10. my ($r1, $r2) = $result.split(':');
    11. %sets{$p1} += $r1;
    12. %sets{$p2} += $r2;
    13. if $r1 > $r2 {
    14. %matches{$p1}++;
    15. } else {
    16. }
    17. }
    18. my @sorted = @names.sort({ %sets{$_} }).sort({ %matches{$_} }).reverse;
    19. for @sorted -> $n {
    20. say "$n has won %matches{$n} matches and %sets{$n} sets";
    21. }

    这会产生输出:

    1. Ana has won 2 matches and 8 sets
    2. Dave has won 2 matches and 6 sets
    3. Charlie has won 1 matches and 4 sets
    4. Beth has won 1 matches and 4 sets

    每个Raku程序都应该以类似于的行开头use v6;。该行告诉编译器程序期望的Perl版本。如果您不小心使用Perl 5运行该文件,您将收到有用的错误消息。6.c是Raku版本的示例。

    语句

    Raku程序由零个或多个语句组成。甲语句用分号或在线的端部的大括号结束:

    词法和块

    my声明一个词法变量,它只在从声明点到块结尾的当前块中可见。如果没有封闭块,则在整个文件的剩余部分(它实际上是封闭块)中可见。块是大括号之间的代码的任何部分{ }。

    符号和标识符

    变量名开始于印记,这是因为这样的非字母数字符号$,@,%,或&—or偶尔双冒号::。Sigils指示变量的结构接口,例如它是否应被视为单个值,复合值,子例程等。在sigil之后出现一个标识符,可以由字母,数字和下划线组成。在字母之间你也可以使用破折号-或撇号’,因此isn’t并且double-click是有效的标识符。

    标量

    Sigils指示变量的默认访问方法。带有@印记的变量可以在位置上访问; 带有%sigil的变量由字符串键访问。的$印记,然而,指示可以包含任何单个值和以任何方式进行访问的通用标量容器中。标量甚至可以包含像a Array或a 这样的复合对象Hash; 的$印记表示这应该被视为一个单一的值,即使在期望多个值(如用一个上下文Array或Hash)。

    文件句柄和赋值

    内置函数open打开一个文件,此处命名scores,并返回一个文件句柄 - 表示该文件的对象。等号将该文件句柄= 分配给左侧的变量,这意味着$file现在存储文件句柄。

    ‘scores.txt’是一个字符串文字。字符串是一段文本,字符串文字是直接出现在程序中的字符串。在这一行中,它是提供给的参数open。

    1. my @names = $file.get.words;

    数组、方法和调用

    右侧调用一个方法 - 一个命名的行为组 - 以get存储在其中的文件句柄命名$file。该get方法从文件中读取并返回一行,删除行结尾。如果您$file在打电话后打印内容get,您将看到第一行不再在那里。words也是一个方法,在返回的字符串上调用get。words将其调用者 - 它所操作的字符串 - 分解为一个单词列表,这里的意思是由空格分隔的字符串。它将单个字符串’Beth Ana Charlie Dave’转换为字符串列表’Beth’, ‘Ana’, ‘Charlie’, ‘Dave’。

    最后,此列表存储在Array中 @names。该@印记标志着声明的变量作为Array。数组存储有序列表。

    1. my %matches;
    2. my %sets;

    散列

    这两行代码声明了两个哈希值。的%印记标记每个变量作为Hash。A Hash是键值对的无序集合。其他编程语言称为哈希表,字典或映射。您可以查询一个哈希表对应于一定的值$key用%hash{$key}。

    在得分计数程序中,%matches存储每个玩家赢得的比赛数量。%sets存储每个玩家赢得的套数。这两个哈希都是由玩家的名字索引的。

    for 和 block

    for生成一个循环,该循环运行由花括号分隔的块一次,用于列表的每个项目,将变量设置$line为每次迭代的当前值。$file.lines生成从文件读取的行列表,从文件scores.txt的第二行开始,因为我们已经调用$file.get过一次,然后一直到文件的末尾。

    在第一次迭代期间,$line将包含字符串Ana Dave | 3:0; 在第二次,Charlie Beth | 3:1等等。

    1. my ($pairing, $result) = $line.split(' | ');

    my可以同时声明多个变量。赋值的右侧是对名为的方法的调用split,将该字符串’ | ‘作为参数传递。

    split将其调用者分解为字符串列表,以便将列表项与分隔符连接将’ | ‘生成原始字符串。

    $pairing获取返回列表的第一项,$result第二项。

    处理完第一行后,$pairing将保持字符串Ana Dave和$result 3:0。

    1. my ($p1, $p2) = $pairing.words;
    2. my ($r1, $r2) = $result.split(':');

    第一个提取并存储变量$p1和中两个玩家的名字$p2。第二个为每个玩家提取结果并将其存储在$r1和中$r2。

    处理完文件的第一行后,变量包含值:cell ‘0’

    然后程序计算每个玩家赢得的次数:

    1. %sets{$p1} += $r1;
    2. %sets{$p2} += $r2;

    += 赋值运算符是一个快捷方式:

    1. %sets{$p1} = %sets{$p1} + $r1;
    2. %sets{$p2} = %sets{$p2} + $r2;

    �Any 和 +=

    += $r1表示将左侧变量中的值增加$ r1。在第一次迭代%sets{$p1}中尚未设置,因此它默认为一个名为的特殊值Any。加法和递增运算符视为Any零的数字; 字符串会自动转换为数字,因为加法是一个数字运算。

    1. if $r1 > $r2 {
    2. %matches{$p1}++;
    3. } else {
    4. %matches{$p2}++;
    5. }

    如果$r1在数值上大于$r2,则%matches{$p1}增加1。如果$r1不大于$r2,则%matches{$p2}递增。正如在这种情况下+=,如果之前不存在任何一个哈希值,它将通过增量操作自动生成。

    后自增和前自增

    `$thing` 是 `$thing += 1` 或 `$thing = $thing + 1` 的缩写,除了表达式的返回值在增量$thing 之前的小异常,而不是递增的值。与许多其他编程语言一样,您可以将其用作前缀。然后它返回递增的值; my $x = 1; say ++$x打印2。

    该行包含三个单独的简单步骤。数组的sort方法返回数组内容的排序版本。但是,数组上的默认排序按其内容排序。要以获胜者优先顺序打印玩家名称,代码必须按照玩家的分数而不是他们的名字对数组进行排序。该sort方法的参数是一个块,用于将数组元素(播放器的名称)转换为要排序的数据。数组项通过主题变量 传入$_。

    您之前已经看过块:for循环→ $line { …​ } 和if语句都在块上运行。块是一个独立的Raku代码片段,带有可选的签名(→ $line 部分)。

    ({ %matches{$_} }),根据赢得的匹配数进行排序。然而Ana和Dave都赢了两场比赛。这种简单的排序并没有考虑赢得的套数,这是决定谁赢得锦标赛的次要标准。

    稳定的排序

    当两个数组项具有相同的值时,sort将它们保留为找到它们的顺序。计算机科学家称之为稳定的。该程序利用Raku的这个属性sort通过两次排序来实现目标:首先是赢得的集合数量(次要标准),然后是赢得的匹配数量。

    在第一个排序步骤之后,名称在顺序中Beth Charlie Dave Ana。在第二个排序步骤之后,它仍然是相同的,因为没有人比其他人赢得更少的比赛但是更多的比赛。这样的情况是完全可能的,特别是在较大的比赛中。

    sort从最小到最大按升序排序。这与所需顺序相反。因此,代码.reverse在第二次排序的结果上调用方法,并将最终列表存储在其中@sorted。

    1. for @sorted -> $n {
    2. say "$n has won %matches{$n} matches and %sets{$n} sets";
    3. }

    say,print 和 put

    为了打印出玩家及其分数,代码循环@sorted,$n依次设置每个玩家的名字。将此代码读作“对于已排序的每个元素,设置$n为元素,然后执行以下块的内容”。say将其参数打印到标准输出(通常是屏幕),然后是换行符。(print如果您不想在最后使用换行符,请使用。)

    请注意,say通过调用.gist方法将截断某些数据结构,因此put如果您想要精确输出则更安全。

    插值

    当您运行该程序时,您将看到它say不会逐字打印该字符串的内容。代替$n它打印变量的内容$n- 存储在其中的玩家的名字$n。代码及其内容的自动替换是插值。此插值仅在由双引号分隔的字符串中发生”…​”。单引号字符串’…​’不进行插值:

    双引号字符串和单引号字符串

    1. my $names = 'things';
    2. say 'Do not call me $names'; # OUTPUT: «Do not call me $names
    3. »
    4. say "Do not call me $names"; # OUTPUT: «Do not call me things
    5. »

    Raku中的双引号字符串可以使用$sigil以及花括号中的代码块来插入变量。由于任何Perl代码都可以出现在花括号中,因此可以通过将它们放在花括号中来插入Arrays和Hashes。

    花括号内的数组使用每个项目之间的单个空格字符进行插值。花括号内的哈希值被插值为一系列线条。每行包含一个键,后跟一个制表符,然后是与该键相关的值,最后是换行符。

    我们现在看一个例子吧。

    在此示例中,您将看到一些特殊语法,可以更轻松地创建字符串列表。这是<…​> 引用词构造。当您在<和>之间放置单词时,它们都被假定为字符串,因此您不需要将它们分别用双引号括起来”…​” 。

    1. say "Math: { 1 + 2 }"; # OUTPUT: «Math: 3
    2. »
    3. my @people = <Luke Matthew Mark>;
    4. say "The synoptics are: {@people}"; # OUTPUT: «The synoptics are: Luke Matthew Mark
    5. »
    6. say "{%sets}
    7. "; # From the table tennis tournament
    8. # Charlie 4
    9. # Ana 8
    10. # Beth 4

    当数组和散列变量直接出现在双引号字符串中(而不是在大括号内)时,如果它们的名称后跟postcircumfix - 一个跟在语句后面的包围对,它们只会被插值。在变量名和postcircumfix之间进行方法调用也没问题。

    1. my @flavors = <vanilla peach>;
    2. say "we have @flavors"; # OUTPUT: «we have @flavors
    3. »
    4. say "we have @flavors[0]"; # OUTPUT: «we have vanilla
    5. »
    6. # so-called "Zen slice"
    7. say "we have @flavors[]"; # OUTPUT: «we have vanilla peach
    8. »
    9. # method calls ending in postcircumfix
    10. say "we have @flavors.sort()"; # OUTPUT: «we have peach vanilla
    11. »
    12. # chained method calls:
    13. say "we have @flavors.sort.join(', ')";
    14. # OUTPUT: «we have peach, vanilla
    15. »

    练习

    1.示例程序的输入格式是多余的:第一行包含所有玩家的名字是不必要的,因为你可以通过查看后续行中的名字来找出参加锦标赛的玩家。

    如果不使用@names变量,如何使程序运行?提示:%hash.keys返回存储的所有密钥的列表%hash。

    答:删除该行my @names = $file.get.words;,然后更改:

    1. my @sorted = @names.sort({ %sets{$_} }).sort({ %matches{$_} }).reverse;

    为:

    除了删除冗余@names变量之外,您还可以使用它来警告播放器是否出现在第一行中未提及的情况,例如由于拼写错误。你会如何修改你的程序来实现这一目标?

    答:更改@names到@valid-players。当通过文件的行循环,请检查$p1和$p2在@valid-players。请注意,对于),您也可以使用(elem)和!(elem)。

    1. ...;
    2. my @valid-players = $file.get.words;
    3. ...;
    4. for $file.lines -> $line {
    5. my ($pairing, $result) = $line.split(' | ');
    6. my ($p1, $p2) = $pairing.split(' ');
    7. if $p1 @valid-players {
    8. say "Warning: '$p1' is not on our list!";
    9. }
    10. if $p2 @valid-players {
    11. say "Warning: '$p2' is not on our list!";
    12. }
    13. }