变量是值或 容器 的符号名称。变量声明或值赋值可能会动态创建一个容器。变量名称可以以一个称为符号的特殊字符开头,然后可选地跟一个名为小符号的第二个特殊字符,再跟一个 标识符

§

有四个符号。标量符号 $、位置符号 @、关联符号 % 和可调用符号 &

符号在语法、类型系统和 容器 之间提供链接。它们为声明变量时最常见的类型约束提供了一个快捷方式,并作为 字符串插值 的标记。 位置符号关联符号 提供类型约束,强制要求基本类型 下标 知道要分派哪些方法。 可调用符号 对函数调用执行相同操作。后者

Sigil类型约束默认类型赋值示例
$Mu(无类型约束)AnyitemInt、Str、Array、Hash
@位置ArraylistList、Array、Range、Buf
%关联HashlistHash、Map、Pair
&可调用可调用itemSub、Method、Block、Routine

示例

my $square = 9 ** 2;
my @array  = 123;   # Array variable with three elements 
my %hash   = London => 'UK'Berlin => 'Germany';

可以通过变量声明中的 is 设置变量绑定的类型。假设我们有一个 FailHash

class FailHash is Hash {
    has Bool $!final = False;
    multi method AT-KEY ( ::?CLASS:D: Str:D \key ){
        fail X::OutOfRange.new(:what("Hash key"), :got(key),
          :range(self.keys)) if $!final && !self.EXISTS-KEY(key);
        callsame  # still not final, so do normal action from Hash 
    }
 
    method finalize() {
        $!final = True
    }
}

然后可以使用 is 定义此类型的 %h 变量

my %h is FailHash = oranges => "round"bananas => "bendy";

然后运行以下代码

say %h<oranges>;
# OUTPUT: «round␤» 
%h.finalize;
say %h<cherry>;
CATCH { default { put .^name''.Str } }
# OUTPUT: «X::OutOfRange: Hash key out of range. Is: cherry, should be in (oranges bananas)» 

有关无 sigil 变量的信息,请参见 无 sigil 变量

项和列表赋值§

有两种类型的变量赋值,项赋值列表赋值

项赋值将右侧的单个值复制到左侧的标量变量中。对除简单标量变量之外的任何内容的赋值都将解析为列表赋值。列表赋值将赋值操作的选择权留给左侧的变量。例如,Array 变量(@ sigil)在列表赋值时清空自身,然后将右侧的所有值逐个复制到自身作为元素。

这两种类型的赋值都使用等号 = 作为运算符,并且都是右结合的,但在运算符优先级上有所不同:项赋值的优先级(级别:项赋值)高于列表赋值(级别:列表前缀)。在分配逗号分隔的元素列表的情况下,这些优先级尤其应与介于两者之间的逗号运算符 , 的优先级进行对比。因此,在没有任何列表定界符括号(或其他用于将列表元素组合在一起的结构)的情况下,项赋值将仅分配指定列表的第一个元素,而不是整个列表。

在赋值表达式中,左侧的上下文决定 = 表示项赋值还是列表赋值。如前所述,项赋值仅限于简单的标量变量。因此,对标量容器(标量上下文)的赋值会触发项赋值,除非通过使用圆括号 ( ) 将标量明确置于列表上下文中

my $a;
$a = 1,2,3;        # item assignment to Scalar 
say $a;            # OUTPUT: «1␤» ( '=' has higher precedence than ',' ) 
 
my $b = 1,2,3;     # item assignment to Scalar (same as preceding example) 
say $b;            # OUTPUT: «1␤» 
 
my $c;
($c= 4,5,6;      # list assignment to Scalar; '( )' is list-contextualizer 
say $c;            # OUTPUT:  «(4,5,6)␤» 
 
(my $d= 4,5,6;   # list assignment to Scalar (same as preceding example) 
say $d;            # OUTPUT:  «(4,5,6)␤»

对列表容器(列表上下文)的赋值始终触发列表赋值

my @e;
@e = 7,8,9;                    # list assignment to Array 
say @e;                        # OUTPUT:  «[7,8,9]␤» 
 
my $f;
($f,) = 7,8,9;                 # list assignment to List with one element 
say $f;                        # OUTPUT:  «7␤» 
say ( ($f,) ).VAR.^name;       # OUTPUT:  «List␤» 
 
# ATTENTION: special declaration syntax! 
my ($g= 7,8,9;               # list assignment to List with one element 
say $g;                        # OUTPUT:  «7␤» 
say ( ($g) ).VAR.^name         # OUTPUT:  «List␤»

上面最后两个示例是简单的解构赋值,它们选择右侧列表的第一个项。有关在变量声明上下文中对解构赋值的更详细讨论,请参见有关 使用词法或包范围声明变量列表 的部分。

解析链式赋值时,会考虑赋值运算符的优先级,以及适用的情况下,它们的右结合性。例如,在下面的示例中,有一个链式赋值语句,包含两个赋值运算符。对 @array 的赋值是一个列表赋值,其优先级低于对标量变量 $num 的项赋值。因此,首先计算涉及对标量变量 $num 的项赋值的赋值表达式。它返回赋值的值 42,该值反过来构成由逗号运算符构建的列表 (42, "str") 的一部分,该运算符的优先级也高于列表赋值。最后,列表 (42, "str") 被列表赋值给 @array

my @array;
@array = my $num = 42"str";   # parsed as @array = ( (my $num = 42), "str ) 
say @array.raku;                # OUTPUT: «[42, "str"]␤» (an Array) 
say $num.raku;                  # OUTPUT: «42␤» (a Num)

这里有一个变体

my ( @foo$bar );
@foo = ($bar= 42"str";       # parsed as @foo = ( $bar = (42, "str") ) 
say $bar.raku;                   # OUTPUT: «$(42, "str")␤» (a List)# 
say @foo.raku;                   # OUTPUT: «[(42, "str"),]␤» (an Array)

在这种情况下,列表上下文化器 ( )$bar 放在列表上下文中,从而触发对标量变量 $bar 的列表赋值。这意味着有两个链式列表赋值,它们都比构建列表 (42, "str") 的逗号运算符 , 优先级低。由于它们的右结合性,首先计算的列表赋值表达式是对 $bar 的赋值,它返回赋值的值 $(42, "str"),即包含一个双元素列表的标量。该值反过来被列表赋值给 @array,这样它就成为一个只有一个元素的数组,即一个列表。

有关优先级和结合性的更多详细信息,请参阅 运算符

无符号变量 §

使用 \ 前缀,可以创建没有符号的变量

my \degrees = pi / 180;
my \θ       = 15 * degrees;

请注意,无符号变量没有关联的 容器。这意味着上面的 degreesθ 实际上直接表示 Num。为了说明这一点,请尝试在定义它之后对其进行赋值

θ = 3# Dies with the error "Cannot modify an immutable Num" 

无符号变量不强制 上下文,因此它们可用于按原样传递某些内容

sub logged(&f|args{
    say('Calling ' ~ &f.name ~ ' with arguments ' ~ args.raku);
    my \result = f(|args);
    #  ^^^^^^^ not enforcing any context here 
    say(&f.name ~ ' returned ' ~ result.raku);
    return |result;
}

无符号变量也可用于绑定。有关更多信息,请参阅 绑定

Twigils§

我们使用术语 twigils,这是对 sigil 的文字游戏,表示它在标识符前面使用两个符号;第二个符号将放在符号和标识符之间,它将与变量的作用域相关,即该变量在哪里定义以及可以在哪里更改。

Twigil范围
仅基于声明符
*动态
?编译时变量
!属性(类成员)
.方法(实际上不是变量)
<索引到匹配对象(实际上不是变量)
^自声明形式位置参数
:自声明形式命名参数
=Pod 变量
~解析器在此词法点看到的子语言

§

此 twigil 用于动态变量,这些变量通过调用者的作用域查找,而不是通过外部作用域查找。请看下面的示例。[1]

my $lexical   = 1;
my $*dynamic1 = 10;
my $*dynamic2 = 100;
 
sub say-all() {
    say "$lexical$*dynamic1$*dynamic2";
}
 
say-all();    # OUTPUT: 1, 10, 100 
 
{
    my $lexical   = 2;
    my $*dynamic1 = 11;
    $*dynamic2    = 101;
 
    say-all(); # OUTPUT: 1, 11, 101 
}
 
say-all();  # OUTPUT: 1, 10, 101 

第一次调用 &say-all 时,它打印“1, 10, 100”,正如人们所期望的那样。然而,第二次调用时,它打印“1, 11, 101”。这是因为 $lexical 不是在调用者的作用域中查找的,而是在定义 &say-all 的作用域中查找的。两个动态变量是在调用者的作用域中查找的,因此具有值 11101。第三次调用 &say-all 时,$*dynamic1 不再是 11,但 $*dynamic2 仍然是 101。这是因为我们在块中声明了一个新的动态变量 $*dynamic1,并且没有像对 $*dynamic2 那样赋值给旧变量。

动态变量与其他变量类型不同,因为引用未声明的动态变量不是编译时错误,而是运行时 Failure,因此只要在将动态变量用于其他任何用途之前检查其定义或在布尔上下文中使用它,就可以将未声明的动态变量用作未声明的变量

sub foo() {
    $*FOO // 'foo';
}
 
say foo# OUTPUT: «foo␤» 
 
my $*FOO = 'bar';
 
say foo# OUTPUT: «bar␤» 

使用 my 声明时,动态变量可以具有词法作用域;使用 our 声明时,动态变量可以具有包作用域。通过 our 引入的动态解析和通过符号表进行的解析是两个正交问题。

§

可以通过 ? twigil 来寻址编译时变量。编译器知道这些变量,并且在编译后不能修改这些变量。一个流行的示例是

say "$?FILE$?LINE"# OUTPUT: "hello.raku: 23" 
                      # if this is the line 23 of a 
                      # file named "hello.raku"

有关这些特殊变量的列表,请参见 编译时变量

§

属性 是每个类实例都存在的变量。可以通过 ! 直接从类内部访问它们

my class Point {
    has $.x;
    has $.y;
 
    method Str() {
        "($!x$!y)"
    }
}

请注意,属性是如何声明为 $.x$.y 的,但仍然通过 $!x$!y 访问的。这是因为在 Raku 中,所有属性都是私有的,并且可以通过使用 $!attribute-name 直接从类内部访问它们。不过,Raku 可能会自动为你生成访问器方法。有关对象、类及其属性的更多详细信息,请参见 面向对象

§

. twigil 实际上根本不是用于变量的。事实上,类似于

my class Point {
    has $.x;
    has $.y;
 
    method Str() {
        "($.x$.y)" # note that we use the . instead of ! this time 
    }
}

只是在 self 上调用方法 xy,这些方法是为你自动生成的,因为你在声明属性时使用了 . twigil。但是,请注意,子类可能会覆盖这些方法。如果你不希望发生这种情况,请改用 $!x$!y

. twigil 执行方法调用这一事实意味着以下内容也是可能的

class SaySomething {
    method a() { say "a"}
    method b() { $.a}
}
 
SaySomething.b# OUTPUT: «a␤»

有关对象、类及其属性和方法的更多详细信息,请参见 面向对象

§

^ twigil 声明块或子例程的形式位置参数;也就是说,$^variable 形式的变量是一种占位符变量。它们可以在裸块中用于声明该块的形式参数。因此,代码中的块

my @powers-of-three = 1,3,9100;
say reduce { $^b - $^a }0|@powers-of-three;
# OUTPUT: «61␤»

有两个形式参数,即 $a$b。请注意,即使 $^b 在代码中出现在 $^a 之前,$^a 仍然是该块的第一个形式参数。这是因为占位符变量按 Unicode 顺序排序。

虽然几乎可以使用任何有效的标识符作为占位符变量,但建议使用短名称或按正确顺序可以轻松理解的名称,以避免让读者感到意外。

普通块和子例程也可以使用占位符变量,但前提是它们没有显式参数列表。

sub say-it    { say $^a} # valid 
sub say-it()  { say $^a} # invalid 
              { say $^a} # valid 
-> $x$y$x { say $^a} # invalid 

占位符变量不能具有类型约束或具有单个大写字母的变量名(不允许这样做是为了能够捕获一些 Perl 惯用语)。

^ twigil 可以与任何 sigil 结合使用,以创建一个具有该 sigil 的占位符变量。该 sigil 将具有其正常的语义效果,如 Sigils 表 中所述。因此,@^array%^hash&^fun 都是有效的占位符变量。

§

: twigil 声明块或子例程的形式命名参数。使用此形式声明的变量也是一种占位符变量。因此,适用于使用 ^ twigil 声明的变量的相同内容也适用于此处(但例外情况是它们不是位置参数,因此不会使用 Unicode 顺序排序)。例如

say { $:add ?? $^a + $^b !! $^a - $^b }45 ) :!add
# OUTPUT: «-1␤»

有关占位符变量的更多详细信息,请参见 ^

关于 ^: 的说明§

与其他 twigil 不同,^: 声明 变量,然后可以引用该变量而无需该 twigil。因此,前面的示例可以写成

say { $:add ?? $^a + $^b !! $a - $b }45 ) :!add      # OUTPUT: «-1␤»

也就是说,一旦你使用 $^a 声明 $a,你就可以在同一作用域中使用 $^a$a 引用该变量。: 也是如此:在声明 $:add 之后,如果你愿意,你可以使用 $add 引用该声明的变量。

在某些情况下,这只是一个方便之处——但在处理嵌套块时,它可能重要得多。例如

{ say $^awith "inner" { say $^a } }("outer");       # OUTPUT: «outer␤inner␤» 
{ say $^awith "inner" { say $a } }("outer");        # OUTPUT: «outer␤outer␤»

第一行声明两个形式位置参数,而第二行只声明一个(但引用它两次)。对于 withforif 等结构,这可能尤其重要,这些结构通常在没有过多考虑它们会创建块的情况下使用。

就像 ^twigil 一样,: twigil 可以与任何 sigil 结合使用;将 : 与 sigil 一起使用将创建一个具有该 sigil 的形式命名参数(应用该 sigil 的语义)。因此,@:array%:hash&:fun 都有效,每个都创建一个具有指定 sigil 的形式命名参数。

§

= twigil 用于访问 Pod 变量。可以通过 Pod 对象访问当前文件中的每个 Pod 块,例如 $=data$=SYNOPSIS=UserBlock。也就是说:一个变量与所需块的名称相同,并且有一个 = twigil。

=begin Foo
...
=end Foo
# after that, $=Foo gives you all Foo-Pod-blocks 

你可以通过 $=pod 将 Pod 树作为分层数据结构访问,其中包含所有 Pod 结构。

请注意,所有这些 $=someBlockName 都支持 PositionalAssociative 角色。

~ twigil §

~ twigil 用于引用子语言(称为 slang)。以下内容很有用

$~MAIN当前主语言(例如,Raku 语句)
$~Quote当前引号语言的根
$~Quasi当前准引号语言的根
$~Regex当前正则表达式语言的根
$~Trans当前音译语言的根
$~P5Regex当前 Perl 正则表达式语言的根

您在当前词法范围内增加这些语言。

use MONKEY-TYPING;
augment slang Regex {  # derive from $~Regex and then modify $~Regex 
    token backslash:std<\Y> { YY };
}

变量声明符和范围§

大多数情况下,使用 my 关键字创建新变量就足够了

my $amazing-variable = "World";
say "Hello $amazing-variable!"# OUTPUT: «Hello World!␤»

但是,有许多声明符可以更改作用域的详细信息,超出了 Twigils 可以做的事情。

声明符效果
my引入词法作用域名称
our引入包作用域名称
has引入属性名称
anon引入对构造私有的名称
state引入词法作用域但持久名称
augment向现有名称添加定义
supersede替换现有名称的定义

还有三个前缀类似于声明符,但作用于预定义变量

前缀效果
temp在范围结束时恢复变量的值
let如果块退出失败,则在范围结束时恢复变量的值
常量声明容器值在其生命周期内不会更改

my 声明符§

使用 my 声明变量会赋予它词法作用域。这意味着它只存在于当前块中。例如

{
    my $foo = "bar";
    say $foo# OUTPUT: «"bar"␤» 
}
say $foo# Exception! "Variable '$foo' is not declared" 

这会引发错误,因为 $foo 仅在我们处于同一作用域时才被定义。

为了在同一语句中创建多个具有词法作用域的变量,请用括号括住变量

my ( $foo$bar );

另请参见 声明具有词法或包作用域的变量列表

此外,词法作用域意味着可以在新作用域中临时重新定义变量

my $location = "outside";
 
sub outer-location {
    # Not redefined: 
    say $location;
}
 
outer-location# OUTPUT: «outside␤» 
 
sub in-building {
    my $location = "inside";
    say $location;
}
 
in-building;    # OUTPUT: «inside␤» 
 
outer-location# OUTPUT: «outside␤»

如果已重新定义变量,则引用外部变量的任何代码将继续引用外部变量。因此,在这里,&outer-location 仍打印外部 $location

sub new-location {
    my $location = "nowhere";
    outer-location;
}
 
new-location# OUTPUT: «outside␤» 

要使 new-location() 打印 nowhere,请使用 the * twigil$location 设为动态变量。在尝试本地作用域后,此 twigil 会使编译器在调用作用域中查找符号,而不是在外部作用域中查找。

my 是子例程的默认作用域,因此 my sub x() {}sub x() {} 执行完全相同的功能。

our 声明符§

our 变量是在周围包的作用域中创建的。它们还在词法作用域中创建别名,因此它们也可以像 my 变量一样使用。

module M {
    our $Var;
    # $Var available here 
}
 
# Available as $M::Var here.

为了同时创建多个具有包作用域的变量,请用括号括住变量

our ( $foo$bar );

另请参阅声明具有词法或包范围的变量列表一节。

声明具有词法 (my) 或包 (our) 范围的变量列表§

一次可以对多个变量进行作用域限定,但 myour 都要求将变量放入括号中

my  (@a,  $s,  %h);   # same as my @a; my $s; my %h; 
our (@aa$ss%hh);  # same as our @aa; our $ss; our %hh;

这可以与解构赋值结合使用。对这样的列表进行的任何赋值都将获取左侧列表中提供的元素数量,并将右侧列表中的相应值分配给它们。任何缺失的元素都将根据变量的类型导致未定义的值。

my (Str $aStr $bInt $c= <a b>;
say [$a$b$c].raku;
# OUTPUT: «["a", "b", Int]␤»

要将列表解构为单个值,请使用 ($var,) 创建一个只有一个元素的列表字面量。当与变量声明符一起使用时,只需在单个变量周围提供括号即可。

sub f { 1,2,3 };
my ($a= f;
say $a.raku;
# OUTPUT: «1␤»

要跳过列表中的元素,请使用匿名状态变量 $

my ($,$a,$,%h= ('a''b', [1,2,3], {:1th});
say [$a%h].raku;
# OUTPUT: «["b", {:th(1)}]␤»

has 声明符§

has 将属性作用域限定为类的实例或角色,并将方法作用域限定为类或角色。has 对于方法是隐含的,因此 has method x() {}method x() {} 执行相同操作。

请参阅面向对象以获取更多文档和一些示例。

anon 声明符§

anon 声明符阻止符号安装在词法作用域、方法表和任何其他地方。

例如,你可以使用它来声明知道自己名称的子例程,但仍然不会安装在作用域中

my %operations =
    half   => anon sub half($x{ $x / 2 },
    square => anon sub square($x{ $x * $x },
    ;
say %operations<square>.name;       # square 
say %operations<square>(8);         # 64

由于它是一个声明符,因此可以在声明任何内容的任何地方应用它,例如对于类甚至无 sigil 变量。

say anon class þ {};     # OUTPUT: «(þ)␤» 
say anon sub þ  { 42 };  # OUTPUT: «&þ␤»

由于这些符号未安装在作用域中,因此不能按名称使用它们。但是,如果需要将它们分配给外部变量并且它们需要知道自己的名称,它们很有用,但可以使用自省来检索此名称。

my $anon-class = anon class {
    has $.bar;
    method equal( ::?CLASS $foo ) {
      return $foo.bar == $.bar;
    }
};
say $anon-class.new:3bar).equal$anon-class.new:3bar ) );
# OUTPUT: «True␤» 

state 声明符§

state 声明词法作用域变量,就像 my 一样。但是,初始化只发生一次,即在正常执行流程中首次遇到初始化时。因此,状态变量将在封闭块或例程的多次执行中保留其值。

因此,子例程

sub a {
    state @x;
    state $l = 'A';
    @x.push($l++);
};
 
say a for 1..6;

每次调用时都会继续递增 $l 并将其附加到 @x。因此,它将输出

[A]
[A B]
[A B C]
[A B C D]
[A B C D E]
[A B C D E F]

由于它们具有词法作用域,因此它们与声明它们的块相关联。

sub foo () {
  for 0..1 {
    state $foo = 1;
    say $foo++;
  }
};
foo;  # OUTPUT: «1␤2␤» 
foo;  # OUTPUT: «1␤2␤» 

在这种情况下,每次进入运行 for 循环的块时都会创建一个新的状态变量,这就是为什么在每次调用 foo 时状态变量都会重置。

这适用于包含代码对象的每个“克隆”,如下例所示

({ state $i = 1$i++.say} xx 3).map: {$_(), $_()}# OUTPUT: «1␤2␤1␤2␤1␤2␤» 

请注意,当同一块代码的同一克隆由多个线程运行时,这不是一个线程安全结构。还要记住,方法每个类只有一个克隆,而不是每个对象都有一个克隆。

my 一样,多个 state 变量的声明必须放在括号中,对于单个变量,括号可以省略。

许多运算符带有隐式绑定,这可能导致远距离操作。

使用 .clone 或强制转换来创建一个可以绑定的新容器。

my @a;
my @a-cloned;
sub f() {
    state $i;
    $i++;
    @a       .push: "k$i" => $i;
    @a-cloned.push: "k$i" => $i.clone;
};
 
f for 1..3;
say @a;        # OUTPUT: «[k1 => 3 k2 => 3 k3 => 3]␤» 
say @a-cloned# OUTPUT: «[k1 => 1 k2 => 2 k3 => 3]␤»

状态变量在所有线程之间共享。结果可能是意外的。

sub code(){ state $i = 0say ++$i$i };
await
    start { loop { last if code() >= 5 } },
    start { loop { last if code() >= 5 } };
 
# OUTPUT: «1␤2␤3␤4␤4␤3␤5␤» 
# OUTPUT: «2␤1␤3␤4␤5␤» 
# many other more or less odd variations can be produced

$ 变量§

除了显式声明的命名状态变量之外,$ 可用作匿名状态变量,而无需显式 state 声明。

say "1-a 2-b 3-c".subst(:g, /\d/{<one two three>[$++]});
# OUTPUT: «one-a two-b three-c␤»

此外,状态变量可以在子例程之外使用。例如,你可以在单行程序中使用 $ 来对文件中的行进行编号。

raku -ne 'say ++$ ~ " $_"' example.txt

词法作用域中的每个对 $ 的引用实际上都是一个单独的变量。

raku -e '{ say ++$; say $++  } for ^5'
# OUTPUT: «1␤0␤2␤1␤3␤2␤4␤3␤5␤4␤»

这就是为什么,如果你需要多次引用同一个 $ 变量(或者,就此而言,任何其他匿名状态变量 @%),一种可能的解决方案是将另一个变量绑定到它,尽管在这个示例中,直接声明状态 $x 而不使用神奇/匿名 $ 变量会更直接

sub foo () {
    my $x := $;
    $x++;
    say $x;
    $x = $x + 1;
}
 
foo() for ^3# OUTPUT: «1␤3␤5␤» 

通常,如果你必须多次引用一个变量,最好声明一个命名状态变量。

请注意,隐式状态声明符仅应用于变量本身,而不应用于可能包含初始化程序的表达式。如果必须完全调用一次初始化程序,则必须提供 state 声明符。

for ^3 {       $ = .say } # OUTPUT: «0␤1␤2␤» 
for ^3 { state $ = .say } # OUTPUT: «0␤» 

@ 变量§

类似于 $ 变量,还有一个 Positional 匿名状态变量 @

sub foo($x{
    say (@).push($x);
}
 
foo($_for ^3;
 
# OUTPUT: «[0] 
#          [0 1] 
#          [0 1 2]␤»

这里的 @ 用括号括起来,以便将表达式与名为 @.push 的类成员变量区分开来。索引访问不需要这种消除歧义,但你需要复制值才能对其执行任何有用的操作。

sub foo($x{
    my $v = @;
    $v[$x= $x;
    say $v;
}
 
foo($_for ^3;
 
# OUTPUT: «[0] 
#          [0 1] 
#          [0 1 2]␤»

$ 一样,在作用域中每次提及 @ 都会引入一个新的匿名数组。

% 变量§

此外,还有一个 Associative 匿名状态变量 %

sub foo($x{
    say (%).push($x => $x);
}
 
foo($_for ^3;
 
# OUTPUT: «{0 => 0} 
#          {0 => 0, 1 => 1} 
#          {0 => 0, 1 => 1, 2 => 2}␤»

消除歧义的相同警告也适用。正如你所料,索引访问也是可能的(通过复制使其有用)。

sub foo($x{
    my $v = %;
    $v{$x} = $x;
    say $v;
}
 
foo($_for ^3;
 
# OUTPUT: «{0 => 0} 
#          {0 => 0, 1 => 1} 
#          {0 => 0, 1 => 1, 2 => 2}␤»

与其他匿名状态变量一样,在给定作用域内每次提及 % 实际上都会引入一个单独的变量。

augment 声明符§

使用 augment,你可以向现有类和语法添加方法,但不能添加属性,前提是你首先激活了 MONKEY-TYPING 实用程序。

由于类通常是our作用域的,因此是全局的,这意味着修改全局状态,这是强烈不建议的。对于几乎所有情况,都有更好的解决方案。

# don't do this 
use MONKEY-TYPING;
augment class Int {
    method is-answer { self == 42 }
}
say 42.is-answer;       # OUTPUT: «True␤»

(在这种情况下,更好的解决方案是使用函数)。

为了获得更好、更安全的示例,这是一个创建类模块以通过添加当前缺少的方法来扩展IO::Path的实用方法,以产生在删除extensionbasename剩余的部分。(请注意,对于该部分的名称或甚至如何构建它,没有明确的开发者共识。)

unit class IO::Barename is IO::Path;
 
method new(|c{
    return self.IO::Path::new(|c);
}
 
use MONKEY-TYPING;
augment class IO::Path {
    method barename {
        self.extension("").basename;
    }
}

temp前缀§

my类似,temp在其作用域结束时恢复变量的旧值。但是,temp不会创建新变量。

my $in = 0# temp will "entangle" the global variable with the call stack 
            # that keeps the calls at the bottom in order. 
sub f(*@c{
    (temp $in)++;
     "<f>\n"
     ~ @c».indent($in).join("\n")
     ~ (+@c ?? "\n" !! "")
     ~ '</f>'
};
sub g(*@c{
    (temp $in)++;
    "<g>\n"
    ~ @c».indent($in).join("\n")
    ~ (+@c ?? "\n" !! "")
    ~ "</g>"
};
print g(g(f(g()), g(), f()));
 
# OUTPUT: «<g> 
#           <g> 
#            <f> 
#             <g> 
#             </g> 
#            </f> 
#            <g> 
#            </g> 
#            <f> 
#            </f> 
#           </g> 
#          </g>␤»

let前缀§

如果块未成功退出,则恢复前一个值。成功退出意味着该块返回一个已定义的值或列表。

my $answer = 42;
 
{
    let $answer = 84;
    die if not Bool.pick;
    CATCH {
        default { say "it's been reset :(" }
    }
    say "we made it 84 sticks!";
}
 
say $answer;

在上述情况下,如果Bool.pick返回 true,则答案将保持为 84,因为该块返回一个已定义的值(say返回True)。否则,die语句将导致块未成功退出,从而将答案重置为 42。

constant前缀§

constant前缀声明它标记的值在其生命周期内不会改变。

constant $pi2 = pi * 2;
$pi2 = 6# OUTPUT: «(exit code 1) Cannot assign to an immutable value␤ 

该值在编译时分配。由于 Raku 模块是自动预编译的,因此在运行程序时不会重新评估模块中定义的常量。有关其他信息,请查看术语页面中的常量部分

类型约束和初始化§

变量通过其绑定的容器具有类型约束,该容器位于声明符和变量名之间。默认类型约束是Mu。您还可以使用特征of来设置类型约束。

    my Int $x = 42;
    $x = 'a string';
    CATCH { default { put .^name''.Str } }
    # OUTPUT: «X::TypeCheck::Assignment: Type check failed in assignment to $x; 
    expected Int but got Str ("a string")␤»

如果标量变量具有类型约束但没有初始值,则使用其绑定的容器的默认值的类型对象对其进行初始化。

my Int $x;
say $x.^name;       # OUTPUT: «Int␤» 
say $x.defined;     # OUTPUT: «False␤»

没有显式类型约束的标量变量被键入为Mu,但默认为Any类型对象。

带有@标记的变量用空Array初始化;带有%标记的变量用空Hash初始化。

变量的默认值可以使用is default特征设置,并通过向其分配Nil重新应用

my Real $product is default(1);
say $product;                       # OUTPUT: «1␤» 
$product *= 5;
say $product;                       # OUTPUT: «5␤» 
$product = Nil;
say $product;                       # OUTPUT: «1␤»

默认已定义变量编译指示§

要强制所有变量具有确定性约束,请使用编译指示use variables :D。该编译指示具有词法作用域,可以使用use variables :_关闭。

use variables :D;
my Int $i;
# OUTPUT: «===SORRY!=== Error while compiling <tmp>␤Variable definition of type Int:D (implicit :D by pragma) requires an initializer ... 
my Int $i = 1# that works 
{ use variables :_my Int $i} # switch it off in this block 

请注意,分配Nil会将变量还原为其默认值,该默认值通常不是确定值,因此会失败约束

use variables :D;
my Int $x = 42;
$x = Nil;
# OUTPUT: «Type check failed in assignment to $x; expected type Int:D cannot be itself…» 

顾名思义,此编译指示适用于变量。

特殊变量§

Raku 尝试对特殊变量使用冗长且描述性的名称。只有三个特别短的特殊变量。

预定义词法变量§

有四个始终可用的特殊变量

变量含义 范围
$_主题变量每个块
$/正则匹配每个子程序/方法
$!异常每个子程序/方法
上次匹配(类似于 $/)当前正则内部

请注意,虽然你可以在任何地方访问 而不会出错,但如果在正则表达式外部访问它,它将包含 Nil.

$_ 变量§

$_ 是主题变量。在每个中创建一个新的变量。它也是没有显式签名的块的默认参数,因此诸如 for @array { ... }given $var { ... } 之类的构造通过调用块将变量的值或值绑定到 $_

for <a b c> { say $_ }  # binds $_ to 'a', 'b' and 'c' in turn 
say $_ for <a b c>;     # same, even though it's not a block 
given 'a'   { say $_ }  # binds $_ to 'a' 
say $_ given 'a';       # same, even though it's not a block

因为 $_ 绑定到迭代的值,所以如果 $_ 绑定到可赋值的东西,你也可以赋值给 $_

my @numbers = ^5;   # 0 through 4 
$_++ for @numbers;  # increment all elements of @numbers 
say @numbers;
 
# OUTPUT: «1 2 3 4 5␤»

CATCH 块将 $_ 绑定到捕获的异常。~~ 智能匹配运算符将右侧表达式的 $_ 绑定到左侧的值。

可以通过省略变量名来缩短对 $_ 调用方法

.say;                   # same as $_.say

m/regex//regex/ 正则匹配和 s/regex/subst/ 替换在 $_ 上工作

say "Looking for strings with non-alphabetic characters...";
for <ab:c d$e fgh ij*> {
    .say if m/<-alpha>/;
}
 
# OUTPUT: «Looking for strings with non-alphabetic characters... 
#          ab:c 
#          d$e 
#          ij*␤»

$/ 变量§

$/ 是匹配变量。在每个例程中创建一个新的变量。它被设置为最后一个 正则表达式 匹配的结果,因此通常包含 Match 类型的对象。

'abc 12' ~~ /\w+/;  # sets $/ to a Match object 
say $/.Str;         # OUTPUT: «abc␤»

Grammar.parse 方法还将调用者的 $/ 设置为结果 Match 对象。对于以下代码

use XML::Grammar# zef install XML 
XML::Grammar.parse("<p>some text</p>");
say $/;
 
# OUTPUT: «「<p>some text</p>」 
#           root => 「<p>some text</p>」 
#            name => 「p」 
#            child => 「some text」 
#             text => 「some text」 
#             textnode => 「some text」 
#           element => 「<p>some text</p>」 
#            name => 「p」 
#            child => 「some text」 
#             text => 「some text」 
#             textnode => 「some text」␤» 

在 6.d 版本之前,你可以使用 $() 快捷方式从 $/ Match 中获取 ast 值(如果该值存在),否则获取 Match 对象的字符串化形式。

'test' ~~ /.../;
# 6.c language only: 
say $(); # OUTPUT: «tes␤»; 
$/.make: 'McTesty';
say $(); # OUTPUT: «McTesty␤»;

此(非)特性已从 6.d 版本开始弃用。

位置属性§

如果 正则表达式 中有捕获组(只需用括号形成),则 $/ 可以具有位置属性。

'abbbbbcdddddeffg' ~~ / a (b+) c (d+ef+) g /;
say $/[0]; # OUTPUT: «「bbbbb」␤» 
say $/[1]; # OUTPUT: «「dddddeff」␤»

还可以通过快捷方式 $0$1$2 等访问它们。

say $0# OUTPUT: «「bbbbb」␤» 
say $1# OUTPUT: «「dddddeff」␤»

要获取所有位置属性,可以使用 $/.list@$/。在 6.d 之前,你还可以使用 @() 快捷方式(括号内没有空格)。

say @$/.join# OUTPUT: «bbbbbdddddeff␤» 
 
# 6.c language only: 
say @().join# OUTPUT: «bbbbbdddddeff␤»

@() 的这种神奇行为已从 6.d 版本开始弃用

命名属性§

如果 正则表达式 中有命名的捕获组,或者如果正则表达式调用了另一个正则表达式,则 $/ 可以具有命名属性。

'I... see?' ~~ / \w+ $<punctuation>=[ <-[\w\s]>+ ] \s* $<final-word> = [ \w+ . ] /;
say $/<punctuation># OUTPUT: «「....」␤» 
say $/<final-word>;  # OUTPUT: «「see?」␤» 

还可以通过快捷方式 $<named> 访问它们。

say $<punctuation># OUTPUT: «「....」␤» 
say $<final-word>;  # OUTPUT: «「see?」␤»

要获取所有命名属性,可以使用 $/.hash%$/。在 6.d 语言之前,你还可以使用 %() 快捷方式(括号内没有空格)。

say %$/.join;       # OUTPUT: «"punctuation     ....final-word  see?"␤» 
 
# 6.c language only 
say %().join;       # OUTPUT: «"punctuation     ....final-word  see?"␤»

此行为已从 6.d 版本开始弃用。

线程安全问题§

因为 $/ 仅在每个例程中定义,所以当你在循环中进行匹配时,实际上是在重复使用同一个 $/。在单线程程序中,这不是问题。但是,如果你要使用 hyperrace 让多个线程并行进行匹配,那么共享“外部”$/ 就会成为一个问题,因为那时它正在线程之间共享!幸运的是,解决方案非常简单:在你进行匹配的范围内定义你自己的 $/。例如,获取一个文本源,对其运行正则表达式,并使用并行执行将其映射到哈希

my %mapped = @source.race.map: {
    my $/;  # need one in this block to prevent racing issues 
    m/foo (.*?) bar (.*)/;  # matches on $_, stores in $/ 
    $0 => $1   # short for $/[0] / $/[1] 
}

$! 变量§

$! 是错误变量。在每个例程中都会创建一个新的变量。如果 try 块或语句前缀捕获到异常,则该异常将存储在 $! 中。如果没有捕获到异常,则 $! 设置为 Nil

请注意,CATCH不会设置 $!。相反,它们将块内的 $_ 设置为捕获到的异常。

另请注意,与 $/ 相同,$! 的使用也适用于相同的线程安全问题。

变量§

请参阅 Match 中的描述。

编译时变量§

所有编译时变量的 twigil 中都包含一个问号。由于是编译时变量,因此无法在运行时更改它们,但是它们对于内省程序非常有价值。最常见的编译时变量如下

$?FILE我在哪个文件中?
$?LINE我在哪一行?[从 1 开始索引]
::?CLASS我在哪个类中?
%?RESOURCES与当前编译单元的“分发”关联的文件。

$?FILE$?LINE 也可从 CallFrame 中分别作为 fileline 方法获得。

%?RESOURCES§

%?RESOURCES 是一个编译时变量,可供 Distribution 的代码使用。

它包含一个哈希,该哈希提供对与当前编译单元的分发关联的文件的编译和运行时访问。此哈希用于访问 Distribution 范围内的静态文件(例如配置文件、模板或数据文件)的特殊存储。

可通过此变量访问的文件需要放置在分发的 resources 目录下

Module-Foo/
├── lib
│   └── Module
│       └── Foo.rakumod
├── META6.json
├── README.md
└── resources
    └── images
        └── foo.jpg

此外,在 META6.json 文件的 "resources" 字段下可以指定到文件的相对路径(从分发的根资源目录开始)

"resources": [
    "images/foo.jpg"
]

每个资源文件都会添加到一个已安装的分发中,并且可以使用类似 Hash 的访问方式访问 %?RESOURCES,返回一个 Distribution::Resources 对象

my $foo-IO = %?RESOURCES<images/foo.jpg>;          # gets an object you can slurp 
my $foo-IO = %?RESOURCES<images/foo.jpg>.absolute# gets an absolute path to a file 
my $foo-IO = %?RESOURCES<images/foo.jpg>.open;     # gets an opened IO::Handle to work with 

请注意,已安装分发中的资源文件的路径和名称可能会被修改,因此不要在除将其用作 %?RESOURCES 变量的键之外的任何其他情况下依赖其值。

%?RESOURCES 变量未实现为普通 Hash,而是 Distribution::Resources 类型的实例,因此不要指望通过打印或使用其他方式来检查其值来查看分发中所有可用的资源文件。相反,使用上述 API 来访问特定文件。

%?RESOURCES 变量只能在模块内访问。如果您想在模块外访问 %?RESOURCES,您需要自己公开该 API。一种方法是在 lib 目录中创建一个例程来返回值

unit module MyLib;
sub my-resources is export {
    %?RESOURCES
}

然后创建一个测试文件,例如 t/resources.t,内容如下

use Test;
use MyLib;
 
my $resources = my-resources;
isa-ok $resourcesHash;

因此,编译时哈希的内容会公开给运行时代码。

内省编译时变量§

以下编译时变量允许更深入的内省

$?PACKAGE我在哪个包中?
$?MODULE, ::?MODULE我在哪个模块中?它包含模块的类型。
$?CLASS我在哪个类中?(作为变量)
$?ROLE我在哪个角色中?(作为变量)
$?TABSTOP在 heredoc 或虚拟边距中,制表符有多少个空格?
$?NL垂直换行符 "\n" 的含义:LF、CR 或 CRLF
$?DISTRIBUTION当前编译单元的发行版。

关于 $?NL 的具体内容,请参阅 换行符编译指示

Rakudo 特有的编译时变量§

这些变量是 Rakudo 特有的,具有所有相应的警告

$?BITS正在编译程序的平台的数据路径位的数量。

&?ROUTINE§

编译时变量 &?ROUTINE 提供有关程序实际所在的例程的内省。它返回一个 Routine 实例,该实例附加到当前例程。它支持 .name 方法以获取被调用例程的名称,以及 .signature 和其他与 Routine 相关的其他方法

sub awesome-sub { say &?ROUTINE.name }
awesome-sub# OUTPUT: «awesome-sub␤»

它还允许递归

my $counter = 10;
sub do-work {
    say 'Calling myself other ' ~ $counter-- ~ ' times';
    &?ROUTINE() if ( $counter > 0 );
}
do-work;

请注意,在 multi 中,&?ROUTINE 指的是当前候选,而不是 multi 整体。

因此,以下递归定义起作用

multi broken-fibonacci($n where * ≤ 1{ $n }
multi broken-fibonacci($n where * > 0{
    &?ROUTINE($n - 1+ &?ROUTINE($n - 2)
}

如果调用,&?ROUTINE 将始终引用第二个 multi 候选,并且永远不会分派到第一个。如果您想对整个 proto 使用自递归,请使用函数名称或 samewith

&?BLOCK§

特殊编译变量 &?BLOCK 的行为类似于 &?ROUTINE,但它允许内省一个代码块。它保存一个 Block,并允许在同一个块内递归

for '.' {
    .Str.say when !.IO.d;
    .IO.dir()».&?BLOCK when .IO.d # lets recurse a little! 
}

$?DISTRIBUTION§

$?DISTRIBUTION 提供对当前编译单元的 Distribution 的访问。这为模块作者提供了一种通过原始相对路径名引用 distribution 中其他文件或查看元数据(通过 .meta 方法)的方法,而无需了解底层文件结构(例如 CompUnit::Repository::Installation 在安装时如何更改文件布局)。

unit module MyFoo;
 
sub module-version {
    say "MyFoo is version:";
    say $?DISTRIBUTION.meta<ver># OUTPUT: «0.0.1␤» 
}

.meta 方法当前具有 verauthapi 的哈希键,

.content 方法在提供 distribution 顶级目录的相对路径时提供对 distribution 中文件的访问。

sub module-source {
    say "MyFoo source code:";
    say $?DISTRIBUTION.content('lib/MyFoo.rakumod');
    # OUTPUT: «IO::Handle<"lib/MyFoo.rakumod".IO>(closed)␤» 
}

请注意,content 示例的输出是一个已关闭的文件句柄。为了实际使用文件的内容,用户可以在同一模块中的另一个例程中将其提取为字符串

sub get-file-content {
    say $?DISTRIBUTION.content('lib/MyFoo.rakumod').open.slurp;
    # OUTPUT: «unit module MyFoo;␤» 
}

动态变量§

所有动态作用域变量都具有 * twigil,并且它们的名称(按照惯例)用大写字母书写。

这些变量与传递给脚本的参数相关。

$*ARGFILES§

一个 IO::ArgFilesIO::CatHandle 的一个空子类),如果它包含任何文件,则使用 @*ARGS 作为源文件,否则使用 $*IN。当使用 $*IN 时,其 :nl-in:chomp:encoding:bin 将在 IO::ArgFiles 对象上设置。

从 6.d 版本开始,$*ARGFILES sub MAIN 始终设置为 $*IN,即使 @*ARGS 不为空。有关示例和更多上下文的详细信息,请参阅 类文档

@*ARGS§

@*ARGS 是一个包含命令行参数的 Str 数组。

&*ARGS-TO-CAPTURE§

可在任何自定义 ARGS-TO-CAPTURE 子例程中使用的动态变量,可用于执行默认参数解析。采用与自定义 ARGS-TO-CAPTURE 子例程预期的相同参数。

&*GENERATE-USAGE§

可在任何自定义 GENERATE-USAGE 子例程中使用的动态变量,可用于执行默认用法消息创建。采用与自定义 GENERATE-USAGE 子例程预期的相同参数。

特殊文件句柄:STDINSTDOUTSTDERR§

有关特殊文件句柄的更多信息,另请参阅 输入和输出 页面和 IO::Special 类。 IO::Handle 包含使用 $*IN 读取标准输入的几个示例。

  • $*IN 标准输入文件句柄,又名 STDIN

  • $*OUT 标准输出文件句柄,又名 STDOUT

  • $*ERR 标准错误文件句柄,又名 STDERR

运行时环境§

这些动态变量包含有关脚本或程序运行环境的信息。

%*ENV§

操作系统环境变量。数字值以 同形异位词 形式提供。

$*REPO§

此变量保存有关已安装/已加载模块的信息。

$*INIT-INSTANT§

$*INIT-INSTANT 是一个 Instant 对象,表示程序启动时间。具体来说,这是核心代码启动的时间,因此 $*INIT-INSTANT 的值可能比程序中执行的 INIT now 甚至 BEGIN now 早几毫秒。

$*TZ§

$*TZ 是一个动态变量,旨在包含一个有关系统本地时区信息的对象。它应该转换为格林尼治时间的 秒数

如果没有明确设置,它只包含一个没有进一步信息的整数值,在首次访问 $*TZ 时设置。在这种情况下,不会看到在此过程中发生的任何夏令时变化。

$*CWD§

它包含 Current Working Directory(当前工作目录)。

$*KERNEL§

$*KERNEL 包含一个 Kernel 实例,它的 .gist 是当前运行的内核。

say $*KERNEL# OUTPUT: «linux (4.4.92.31.default)␤»

$*DISTRO§

此对象(类型为 Distro)包含有关当前操作系统发行版的信息。例如

say "Some sort of Windows" if $*DISTRO.is-win;

$*DISTRO.name 采用一组取决于操作系统的值。这些名称会因版本和实现而异,因此在程序中使用它们之前,您应该仔细检查并进行测试;由于这些名称是由实现定义的,而不是规范中的,它们可能会随时发生变化。

使用 say 显示 $*DISTRO gist

say $*DISTRO# OUTPUT: «debian (9.stretch)␤»

这显示了有关操作系统及其正在使用的版本的其他信息,但事实上,此变量包含对创建可移植程序有用的信息,例如路径分隔符

say $*DISTRO.raku;
# OUTPUT: «Distro.new(release => "42.3", is-win => Bool::False, 
#          path-sep => ":", name => "opensuse", 
#          auth => "https://www.opensuse.org/", version => v42.3, 
#          signature => Blob, desc => "2018-12-13T08:50:59.213619+01:00")␤»

$*VM§

此变量包含运行代码的当前虚拟机,以及有关上述 VM 内部工作原理的其他信息。

say $*VM.precomp-ext" "$*VM.precomp-target# OUTPUT: «moarvm mbc␤»

例如,这两个方法将显示预编译字节码脚本中使用的扩展和使用的目标。这是在 Moar 虚拟机中找到的,但它也可能因版本和实现而异。其他 VM,例如 Java,将显示不同的值。$*VM.config 包括用于创建虚拟机的所有配置值,例如

say $*VM.config<versionmajor>"."$*VM.config<versionminor>;
# OUTPUT: «2018.11␤»

这是虚拟机版本,通常与解释器和整体 Raku 环境中使用的版本相同。

$*RAKU§

Raku 类的此对象包含有关 Raku 语言当前实现的信息

say $*RAKU.compiler.version# OUTPUT: «v2020.01␤»

但其要点包括语言名称,后跟编译器主版本

say $*RAKU# OUTPUT: «Raku (6.d)␤»

它字符串化为 Raku

$*RAKU.put# OUTPUT: «Raku␤»

注意:在 Rakudo 版本 2020.1 之前,此信息只能通过 $*PERL 变量获得。自 Rakudo 版本 2020.1 起,它可以通过 $*RAKU$*PERL 变量获得。

$*PERL§

在可预见的未来,与 $*RAKU 相同。将在某个时候弃用。

$*PID§

包含一个描述当前进程标识符(取决于操作系统)的整数的对象。

$*PROGRAM-NAME§

这包含当前可执行文件的路径,就像在命令行中输入的那样,或者如果使用 -e 标志调用 raku,则为 -e

$*PROGRAM§

包含要执行的 Raku 程序的位置(以 IO::Path 对象的形式)。

&*EXIT§

这是一个 Callable,其中包含执行 exit() 调用时将执行的代码。旨在在 Raku 嵌入在其他语言运行时(例如 Perl 中的 Inline::Perl6)的情况下使用。

$*EXIT§

使用 $*EXIT 通常仅在 END 块中才有意义。它包含当前已知的 exit() 值:如果发生异常,则为 1;如果未发生异常,则为 0;如果执行了 exit(N),则为 N

$*EXIT 的支持已添加到 Rakudo 编译器版本 2023.02 中。

$*EXCEPTION§

使用 $*EXCEPTION 通常仅在 END 块中才有意义。如果程序因抛出异常而结束,它将包含一个实例化的 Exception 对象。否则将包含异常类型对象。

$*EXCEPTION 的支持已添加到 Rakudo 编译器版本 2023.02 中。

$*EXECUTABLE§

包含当前正在运行的 raku 可执行文件的 IO::Path 绝对路径。

$*EXECUTABLE-NAME§

包含当前正在运行的 Raku 可执行文件的名称。(例如 raku-p、raku-m)。优先使用 $*EXECUTABLE,因为它不能保证 raku 可执行文件在 PATH 中。

$*USAGE§

这是一个 Str 类型的对象,其中包含从 sub MAINsub USAGE 中可用的 MAIN 子例程的签名生成的默认用法消息。该变量是只读的

sub MAIN($a:$bUInt :$ehehe{
    say $*USAGE# OUTPUT: «Usage:␤  my-script.raku [-a=<Int>] [-b=<Str>] [--<opts>=...]» 
}

它只能在 MAIN 子程序中访问。

$*USER§

一个 Allomorph,其中包含有关正在运行程序的用户的信息。如果将其视为字符串,它将评估为用户名;如果将其视为数字,它将评估为数字用户 ID。

$*GROUP§

一个 Allomorph,其中包含正在运行程序的用户的初级组。如果将其视为字符串,它将评估为组名;如果将其视为数字,它将评估为数字组 ID。

$*HOMEDRIVE§

包含有关正在 Windows 上运行程序的用户“主驱动器”的信息。它在其他操作系统中未定义。

$*HOMEPATH§

包含有关正在 Windows 上运行程序的用户目录的路径的信息。它在其他操作系统中未定义。

$*HOME§

包含一个 IO::Path 对象,表示正在运行程序的用户的“主目录”。如果已设置,则使用 %*ENV<HOME>。

在 Windows 上,使用 %*ENV<HOMEDRIVE> ~ %*ENV<HOMEPATH>。如果无法确定主目录,它将为 Any

$*SPEC§

包含适用于程序运行平台的适当 IO::Spec 子类。这是一个用于操作系统的更高级别类;例如,在 Linux 的情况下,它将返回 Unix(以 IO::Spec 类用于当前实现的形式)。

$*TMPDIR§

这是一个 IO::Path 对象,表示“系统临时目录”,由 .tmpdir IO::Spec::* 方法 确定。

$*THREAD§

包含一个 Thread 对象,表示当前正在执行的线程。

$*SCHEDULER§

这是一个 ThreadPoolScheduler 对象,表示当前默认调度程序。

默认情况下,这会对使用该调度程序(例如 PromiseSupply)的方法 .hyper.race 和其他线程池类施加最多 64 个线程的限制。但是,这取决于实现,并且可能会发生更改。要更改最大线程数,可以在运行 raku 之前设置环境变量 RAKUDO_MAX_THREADS,或者在使用它们之前创建一个作用域副本,并更改默认值。

my $*SCHEDULER = ThreadPoolScheduler.newmax_threads => 128 );

此行为未在规范测试中进行测试,并且可能会发生更改。

$*SAMPLER§

当前 Telemetry::Sampler 用于创建系统状态快照。仅在已加载 Telemetry 时可用。

运行时变量§

这些变量会影响某些函数的行为,在某些情况下,其值可在运行时更改。

$*DEFAULT-READ-ELEMS§

影响 IO::Handle.read 默认读取的字节数。其默认值为 65536。

$*COLLATION§

这是一个 Collation 对象,可用于配置 Unicode 校对级别。

$*RAT-OVERFLOW§

Rakudo 编译器 2022.02 版本中可用。

单独使用 Rats 很好,直到精度用尽。$*RAT-OVERFLOW 动态变量指定当 Rat 精度溢出时应执行的行为。默认情况下,它设置为 Num,这意味着它将恢复为使用(有损)浮点数。

你还可以将其设置为 FatRat,这将导致当 Rat 精度用尽时自动升级到 FatRat。你还可以指定 Failure(使其失败)、Exception(使其抛出异常)或 CX::Warn(使其在降级到 Num 时发出警告)。

要在全局范围内激活此项以升级到 FatRat

INIT $*RAT-OVERFLOW = FatRat;

仅针对词法作用域激活此项

my $*RAT-OVERFLOW = FatRat;

它是如何工作的?$*RAT-OVERFLOW 变量应该包含一个类或实例,当 Rat 溢出时将在其上调用 UPGRADE-RAT 方法。因此,你可以通过创建一个包含 UPGRADE-RAT 方法的类来引入自己的行为。

此类方法应接受两个整数值:一个用于分子,另一个用于分母。例如(不太严肃),如果非常大,则将结果值转换为 Inf;如果非常小,则转换为 0

class ZeroOrInf {
    method UPGRADE-RAT(Int $nuInt $de{
        $nu > $de ?? Inf !! 0
    }
}

$*TOLERANCE§

=~= 运算符和依赖于它的任何运算使用的变量,用于决定两个值是否近似相等。默认为 1e-15

命名约定§

了解我们的命名约定有助于直接理解代码的作用。但是,目前还没有(也可能永远不会有)官方列表;不过,我们列出了一些广泛认可的约定。

  • 如果可以找到一个好名称,则内置库中的子例程和方法会尝试使用单字名称。在名称由两个或更多个单词组成的情况下,它们之间用连字符分隔。

  • 复合词被视为一个单词,因此为 substrsubbufdeepmap(就像我们在英语中写“starfish”,而不是“star fish”)。

  • 在特殊时间自动为您调用的子例程和方法都用大写字母书写。这包括 MAIN 子例程、AT-POS 和用于实现容器类型的相关方法,以及 BUILDDESTROY

  • 类型名称采用驼峰式大小写,但本机类型除外,本机类型采用小写字母。对于例外情况,您可以通过以下方式记住:它们以更紧凑的方式存储,因此它们的名称看起来也更小。

  • 内置动态变量和编译时变量始终采用大写字母,例如 $*OUT$?FILE

  • 来自 MOP 和其他内部结构的方法使用 "_" 来分隔多个单词,例如 add_method

1 [↑] 以下示例无法在 REPL 中正确运行,会产生有关找不到动态变量的错误。请通过将其复制粘贴到文件中,然后运行该文件来对其进行测试。