Wait the light to fall

第二十章. 高级话题

焉知非鱼

声明

本章翻译仅用于 Raku 学习和研究, 请支持电子版或纸质版

第二十章. 高级话题

In such a short book I don’t have enough pages to show you everything that you can do. This chapter is a brief survey of some of the features I would have liked to explain in more detail. You now know these exist and you can investigate them further on your own.

在这么短的书中,我没有足够的页面向你展示你可以做的一切。本章简要介绍了一些我希望更详细解释的功能。你现在知道这些存在,你可以自己进一步研究它们。

单行

You can run raku one-liners. These are programs that you compose completely on the command line. The -eswitch takes an argument that is the program:

你可以运行 raku 单行。这些完全是你在命令行上编写的程序。 -e 开关接受一个参数,即程序:

% raku -e 'put "Hello Raku"'
Hello Raku

The -n switch runs the program once for each line of input. The current line is in $_. This one uppercases and outputs the line:

-n 开关为每行输入运行一次程序。当前行是 $_。这个单行大写一行并输出该行:

% raku -n -e '.uc.put' *.pod

You can load a module with -M:

你可以使用 -M 加载模块:

% raku -MMath::Constants -e 'put α'
0.0072973525664

Declarator Block Comments

The parser doesn’t discard all comments. It remembers special comments and attaches them to the subroutine. #| comments attach themselves to the subroutine after them and #= comments attach themselves to the subroutine before them. These comments are available through the .WHY meta-method:

解析器不会丢弃所有的注释。它会记住特殊注释并将它们附加到子例程中。 #| 注释将它们附加到它们之后的子例程中,并且 #= 注释将它们自身附加到它们之前的子例程中。这些注释可通过 .WHY 元方法获得:

#| Hamadryas is a sort of butterfly
class Hamadryas {

    #| Flap makes the butterfly go
    method flap () {

        }
    }

Hamadryas.WHY.put;
Hamadryas.^find_method('flap').WHY.put;

The output is the combination of all the comments attached to that subroutine:

输出是附加到该子例程的所有注释的组合:

Hamadryas is a sort of butterfly
Flap makes the butterfly go

This is the sort of thing that’s handy in an integrated development environment to grab a description of the thing you are trying to use. It’s also useful when you are debugging something—that is, it’s useful if the developer documented their code.

在集成开发环境中,这种方法很方便,可以获取你尝试使用的内容的描述。当你调试某些内容时,它也很有用 - 也就是说,如果开发人员记录了他们的代码,那么它很有用。

Feed Operators

The feed operators decide which way information flows. Here’s a list-processing pipeline that has a .grep, a .sort, and finally a .map. What they do doesn’t matter as much as their order:

feed 操作符决定信息的流向。这是一个列表处理管道,它有一个 .grep,一个 .sort,最后一个 .map。他们所做的事与顺序无关紧要:

my @array = @some-array
    .grep( *.chars > 5 )
    .sort( *.fc )
    .map( *.uc )
    ;

The final step is farthest away from the assignment. You might not like that. The leftward feed operator allows you to write this in a way where the data flows in one direction. This flows bottom to top into the new variable:

最后一步是离赋值最远的。你可能不喜欢那样。向左的 feed 操作符允许你以数据在一个方向上流动的方式编写代码。下面这个从底部到顶部流入新到变量:

my @array <==
    map(  *.uc         ) <==
    sort( *.fc         ) <==
    grep( *.chars > 5  ) <==
    @some-array
    ;

Notice that the assignment operator disappeared because the feed operator took care of that.

请注意,赋值运算符已消失,因为 feed 运算符负责处理了。

The rightward feed operator goes the other way. The new variable is at the end this time. This is the same thing in the other direction:

向右的 feed 操作符走另一条路。这次这个新变量在最后。在另一方向上也是如此:

@some-array
    ==> grep( *.chars > 5  )
    ==> sort( *.fc         )
    ==> map(  *.uc         )
    ==> my @array;

Destructuring Signatures

You can group parameters with square brackets to create a subsignature. Inside the [] you can break down the aggregate into a smaller signature:

你可以使用方括号对参数进行分组以创建子签名。在 []内部,你可以将总体分解为较小的签名:

sub show-the-arguments ( $i, [$j, *@args] ) {  # slurpy
    put "The arguments are i: $i j: $j and @args[]";
    }

my @a = ( 3, 7, 5 );
show-the-arguments( 1, @a );

With that, $i gets the first parameter and the [] gets the rest. The [] destructures the remaining arguments into $j and @args.

有了它,$i 获得第一个参数,[] 获得其余参数。 [] 将剩余的参数解构为 $j@args

Defining Your Own Operators

You can create new operators. Almost all of the things that we call “operators” are methods.

The and ↑↑ represent Knuth arrows. These are higher levels of exponentiation:

你可以创建新的运算符。几乎所有我们称之为“运算符”的东西都是方法。

↑↑ 代表高德纳箭头。这些是更高的取幂水平:

multi infix:<↑> ( Int:D \n, Int:D \m  --> Int:D )
    is equiv(&infix:<**>)
    is assoc<right>
    { n ** m }

proto infix:<↑↑> ( Int:D \n, Int:D \m --> Int:D )
    is tighter(&infix:<↑>)
    is assoc<right>
    { * }
multi infix:<↑↑> ( \n,  0 ) { 1 }
multi infix:<↑↑> ( \n,  1 ) { n }
multi infix:<↑↑> ( \n, \m ) { [↑] n xx m }

put 2↑3;  # 2 ** 3 = 8
put 2↑↑3; # 2 ** 2 ** 2 = 2 ** 4 = 16

Notice that the definitions allow you to set traits for precedence and associativity. As with other subroutines these are lexically scoped, so they won’t affect other parts of your program.

请注意,这些定义允许你设置优先级和关联性的特征。与其他子例程一样,它们是词法作用域的,因此它们不会影响程序的其他部分。

Perl 5 Patterns

If you like Perl 5 patterns better, or already have some good ones that you’d like to reuse, you can do that. The :Perl5 adverb tells the match operator to interpret the pattern as a Perl 5 regular expression:

如果你更喜欢 Perl 5 模式,或者已经有一些你想要重用的好模式,你可以这样做。 :Perl5 副词告诉匹配操作符将模式解释为 Perl 5 正则表达式:

my $file = ...;
for $file.IO.lines {
    next unless m:Perl5/\A\s+#/;  # no quoting the # in Perl 5
    .put;
    }

Shaped Arrays

Want a multidimensional matrix? You can create a shaped array that knows how wide it is in any dimension. Use the ; to separate the dimensions:

想要一个多维矩阵?你可以创建一个定形数组,知道它在任何维度上的宽度。使用 ; 分开维度:

my @array[2;2];
say @array; # [[(Any) (Any)] [(Any) (Any)]]

@array[1;0] = 'Hamadryas';
say @array; # [[(Any) (Any)] [Hamadryas (Any)]]

my $n = 0;
my $m = 1;

@array[$n;$m] = 'Borbo';
say @array; # [[(Any) Borbo] [Hamadryas (Any)]]

You can extend this to higher dimensions:

你可以将此扩展到更高的维度:

my @array[2;2;3];

The :shape adverb can describe the size in each dimension:

:shape 副词可以描述每个维度的大小:

my @array = Array.new: :shape(3,3);

Once you set the limits in each dimension the size is fixed. This means that you can create fixed-size one-dimensional arrays. You won’t be able to use operaters that increase or decrease the number of elements:

在每个维度中设置限制后,大小就固定了。这意味着你可以创建固定大小的一维数组。你将无法使用增加或减少元素数量的操作符:

my @array[5];

Typed Containers

The container types (List, Array, Hash, and so on) can limit their elements to a particular type. There are a few ways that you can constrain these. Consider this example:

容器类型(List, Array, Hash等)可以将其元素限制为特定类型。有几种方法可以约束这些。考虑这个例子:

my Int @array = 1, 2, 3;
@array.push: 'Hamadryas';

Since a Str is not an Int the .push fails:

由于字符串不是Int,因此 .push 失败:

Type check failed in assignment to @array

That form types the @array variable. The type is actually Array[Int]. You can also bind to the object you construct directly:

该形式键入 @array 变量。该类型实际上是 Array [Int]。你还可以绑定到直接构造的对象:

my @array := Array[Int].new: 1, 3, 7;

You can create Hashes with objects for keys and many other interesting constraints.

你可以使用对象创建散列以及许多其他有趣的约束。

NativeCall

There’s a builtin foreign function interface named NativeCall. You use the is native trait to specify the external library. This one connects your program to the argumentless flap routine in libbutterfly:

有一个名为 NativeCall 的内置外部函数接口。你使用 is native trait 指定外部库。这个程序将你的程序连接到 libbutterfly 中的无参数 flap 例程:

use NativeCall;
sub flap() is native('butterfly') { * }

There are ways to tell NativeCall how to translate data structures to “native” types and the other way around.

有办法告诉 NativeCall 如何将数据结构转换为“原生”类型,反之亦然。

The with Topicalizer

The with keyword sets the topic. In the postfix form you can use it so you don’t have to repeat a long variable name:

with 关键字设置主题。你可以在后缀形式中使用它,以使你不必重复长变量名称:

put "$_ has {.chars}" with $some-very-long-name;

There’s a Block form that’s similar to if-elsif-else but sets the topic to the result of the condition. Instead of looking for True or False it tests for definedness. In each of these the topic inside the Block is the result of the respective .index:

有一个与 if-elsif-else 类似的 Block 形式,但将主题设置为条件的结果。它不是寻找 TrueFalse,而是测试定义。 Block 里的每个这样的主题是相应的 .index 的结果:

my $s = 'Hamadryas';

  with $s.index: 'a' { $s.substr( $_, 2 ).put }
orwith $s.index: 'm' { put 'Found m' }
orwith $s.index: 'H' { fail "Why is there an H at $_?"  }