变量是值或 容器 的符号名称。变量声明或值赋值可能会动态创建一个容器。变量名称可以以一个称为符号的特殊字符开头,然后可选地跟一个名为小符号的第二个特殊字符,再跟一个 标识符。
§
有四个符号。标量符号 $
、位置符号 @
、关联符号 %
和可调用符号 &
。
符号在语法、类型系统和 容器 之间提供链接。它们为声明变量时最常见的类型约束提供了一个快捷方式,并作为 字符串插值 的标记。 位置符号 和 关联符号 提供类型约束,强制要求基本类型 下标 知道要分派哪些方法。 可调用符号 对函数调用执行相同操作。后者
Sigil | 类型约束 | 默认类型 | 赋值 | 示例 |
---|---|---|---|---|
$ | Mu(无类型约束) | Any | item | Int、Str、Array、Hash |
@ | 位置 | Array | list | List、Array、Range、Buf |
% | 关联 | Hash | list | Hash、Map、Pair |
& | 可调用 | 可调用 | item | Sub、Method、Block、Routine |
示例
my = 9 ** 2;my = 1, 2, 3; # Array variable with three elementsmy = London => 'UK', Berlin => 'Germany';
可以通过变量声明中的 is
设置变量绑定的类型。假设我们有一个 FailHash
类
is Hash
然后可以使用 is
定义此类型的 %h
变量
my is FailHash = oranges => "round", bananas => "bendy";
然后运行以下代码
say <oranges>;# OUTPUT: «round».finalize;say <cherry>;CATCH# OUTPUT: «X::OutOfRange: Hash key out of range. Is: cherry, should be in (oranges bananas)»
有关无 sigil 变量的信息,请参见 无 sigil 变量。
项和列表赋值§
有两种类型的变量赋值,项赋值和列表赋值。
项赋值将右侧的单个值复制到左侧的标量变量中。对除简单标量变量之外的任何内容的赋值都将解析为列表赋值。列表赋值将赋值操作的选择权留给左侧的变量。例如,Array
变量(@
sigil)在列表赋值时清空自身,然后将右侧的所有值逐个复制到自身作为元素。
这两种类型的赋值都使用等号 =
作为运算符,并且都是右结合的,但在运算符优先级上有所不同:项赋值的优先级(级别:项赋值)高于列表赋值(级别:列表前缀)。在分配逗号分隔的元素列表的情况下,这些优先级尤其应与介于两者之间的逗号运算符 ,
的优先级进行对比。因此,在没有任何列表定界符括号(或其他用于将列表元素组合在一起的结构)的情况下,项赋值将仅分配指定列表的第一个元素,而不是整个列表。
在赋值表达式中,左侧的上下文决定 =
表示项赋值还是列表赋值。如前所述,项赋值仅限于简单的标量变量。因此,对标量容器(标量上下文)的赋值会触发项赋值,除非通过使用圆括号 ( )
将标量明确置于列表上下文中
my ;= 1,2,3; # item assignment to Scalarsay ; # OUTPUT: «1» ( '=' has higher precedence than ',' )my = 1,2,3; # item assignment to Scalar (same as preceding example)say ; # OUTPUT: «1»my ;() = 4,5,6; # list assignment to Scalar; '( )' is list-contextualizersay ; # OUTPUT: «(4,5,6)»(my ) = 4,5,6; # list assignment to Scalar (same as preceding example)say ; # OUTPUT: «(4,5,6)»
对列表容器(列表上下文)的赋值始终触发列表赋值
my ;= 7,8,9; # list assignment to Arraysay ; # OUTPUT: «[7,8,9]»my ;(,) = 7,8,9; # list assignment to List with one elementsay ; # OUTPUT: «7»say ( (,) ).VAR.^name; # OUTPUT: «List»# ATTENTION: special declaration syntax!my () = 7,8,9; # list assignment to List with one elementsay ; # OUTPUT: «7»say ( () ).VAR.^name # OUTPUT: «List»
上面最后两个示例是简单的解构赋值,它们选择右侧列表的第一个项。有关在变量声明上下文中对解构赋值的更详细讨论,请参见有关 使用词法或包范围声明变量列表 的部分。
解析链式赋值时,会考虑赋值运算符的优先级,以及适用的情况下,它们的右结合性。例如,在下面的示例中,有一个链式赋值语句,包含两个赋值运算符。对 @array
的赋值是一个列表赋值,其优先级低于对标量变量 $num
的项赋值。因此,首先计算涉及对标量变量 $num
的项赋值的赋值表达式。它返回赋值的值 42
,该值反过来构成由逗号运算符构建的列表 (42, "str")
的一部分,该运算符的优先级也高于列表赋值。最后,列表 (42, "str")
被列表赋值给 @array
my ;= my = 42, "str"; # parsed as @array = ( (my $num = 42), "str )say .raku; # OUTPUT: «[42, "str"]» (an Array)say .raku; # OUTPUT: «42» (a Num)
这里有一个变体
my ( , );= () = 42, "str"; # parsed as @foo = ( $bar = (42, "str") )say .raku; # OUTPUT: «$(42, "str")» (a List)#say .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(, |args)
无符号变量也可用于绑定。有关更多信息,请参阅 绑定。
Twigils§
我们使用术语 twigils,这是对 sigil 的文字游戏,表示它在标识符前面使用两个符号;第二个符号将放在符号和标识符之间,它将与变量的作用域相关,即该变量在哪里定义以及可以在哪里更改。
Twigil | 范围 |
---|---|
无 | 仅基于声明符 |
* | 动态 |
? | 编译时变量 |
! | 属性(类成员) |
. | 方法(实际上不是变量) |
< | 索引到匹配对象(实际上不是变量) |
^ | 自声明形式位置参数 |
: | 自声明形式命名参数 |
= | Pod 变量 |
~ | 解析器在此词法点看到的子语言 |
§
此 twigil 用于动态变量,这些变量通过调用者的作用域查找,而不是通过外部作用域查找。请看下面的示例。[1]
my = 1;my = 10;my = 100;sub say-all()say-all(); # OUTPUT: 1, 10, 100say-all(); # OUTPUT: 1, 10, 101
第一次调用 &say-all
时,它打印“1, 10, 100
”,正如人们所期望的那样。然而,第二次调用时,它打印“1, 11, 101
”。这是因为 $lexical
不是在调用者的作用域中查找的,而是在定义 &say-all
的作用域中查找的。两个动态变量是在调用者的作用域中查找的,因此具有值 11
和 101
。第三次调用 &say-all
时,$*dynamic1
不再是 11
,但 $*dynamic2
仍然是 101
。这是因为我们在块中声明了一个新的动态变量 $*dynamic1
,并且没有像对 $*dynamic2
那样赋值给旧变量。
动态变量与其他变量类型不同,因为引用未声明的动态变量不是编译时错误,而是运行时 Failure
,因此只要在将动态变量用于其他任何用途之前检查其定义或在布尔上下文中使用它,就可以将未声明的动态变量用作未声明的变量
sub foo()say foo; # OUTPUT: «foo»my = '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
请注意,属性是如何声明为 $.x
和 $.y
的,但仍然通过 $!x
和 $!y
访问的。这是因为在 Raku 中,所有属性都是私有的,并且可以通过使用 $!attribute-name
直接从类内部访问它们。不过,Raku 可能会自动为你生成访问器方法。有关对象、类及其属性的更多详细信息,请参见 面向对象。
§
.
twigil 实际上根本不是用于变量的。事实上,类似于
my
只是在 self
上调用方法 x
和 y
,这些方法是为你自动生成的,因为你在声明属性时使用了 .
twigil。但是,请注意,子类可能会覆盖这些方法。如果你不希望发生这种情况,请改用 $!x
和 $!y
。
.
twigil 执行方法调用这一事实意味着以下内容也是可能的
SaySomething.b; # OUTPUT: «a»
有关对象、类及其属性和方法的更多详细信息,请参见 面向对象。
§
^
twigil 声明块或子例程的形式位置参数;也就是说,$^variable
形式的变量是一种占位符变量。它们可以在裸块中用于声明该块的形式参数。因此,代码中的块
my = 1,3,9…100;say reduce , 0, |;# OUTPUT: «61»
有两个形式参数,即 $a
和 $b
。请注意,即使 $^b
在代码中出现在 $^a
之前,$^a
仍然是该块的第一个形式参数。这是因为占位符变量按 Unicode 顺序排序。
虽然几乎可以使用任何有效的标识符作为占位符变量,但建议使用短名称或按正确顺序可以轻松理解的名称,以避免让读者感到意外。
普通块和子例程也可以使用占位符变量,但前提是它们没有显式参数列表。
sub say-it # validsub say-it() # invalid# valid-> , , # invalid
占位符变量不能具有类型约束或具有单个大写字母的变量名(不允许这样做是为了能够捕获一些 Perl 惯用语)。
^
twigil 可以与任何 sigil 结合使用,以创建一个具有该 sigil 的占位符变量。该 sigil 将具有其正常的语义效果,如 Sigils 表 中所述。因此,@^array
、%^hash
和 &^fun
都是有效的占位符变量。
§
:
twigil 声明块或子例程的形式命名参数。使用此形式声明的变量也是一种占位符变量。因此,适用于使用 ^
twigil 声明的变量的相同内容也适用于此处(但例外情况是它们不是位置参数,因此不会使用 Unicode 顺序排序)。例如
say ( 4, 5 ) :!add# OUTPUT: «-1»
有关占位符变量的更多详细信息,请参见 ^。
关于 ^
和 :
的说明§
与其他 twigil 不同,^
和 :
声明 变量,然后可以引用该变量而无需该 twigil。因此,前面的示例可以写成
say ( 4, 5 ) :!add # OUTPUT: «-1»
也就是说,一旦你使用 $^a
声明 $a
,你就可以在同一作用域中使用 $^a
或 $a
引用该变量。:
也是如此:在声明 $:add
之后,如果你愿意,你可以使用 $add
引用该声明的变量。
在某些情况下,这只是一个方便之处——但在处理嵌套块时,它可能重要得多。例如
("outer"); # OUTPUT: «outerinner»("outer"); # OUTPUT: «outerouter»
第一行声明两个形式位置参数,而第二行只声明一个(但引用它两次)。对于 with
、for
和 if
等结构,这可能尤其重要,这些结构通常在没有过多考虑它们会创建块的情况下使用。
就像 ^
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
都支持 Positional
和 Associative
角色。
~
twigil §
~
twigil 用于引用子语言(称为 slang)。以下内容很有用
$~MAIN | 当前主语言(例如,Raku 语句) |
$~Quote | 当前引号语言的根 |
$~Quasi | 当前准引号语言的根 |
$~Regex | 当前正则表达式语言的根 |
$~Trans | 当前音译语言的根 |
$~P5Regex | 当前 Perl 正则表达式语言的根 |
您在当前词法范围内增加
这些语言。
use MONKEY-TYPING;augment
变量声明符和范围§
大多数情况下,使用 my
关键字创建新变量就足够了
my = "World";say "Hello $amazing-variable!"; # OUTPUT: «Hello World!»
但是,有许多声明符可以更改作用域的详细信息,超出了 Twigils 可以做的事情。
声明符 | 效果 |
---|---|
my | 引入词法作用域名称 |
our | 引入包作用域名称 |
has | 引入属性名称 |
anon | 引入对构造私有的名称 |
state | 引入词法作用域但持久名称 |
augment | 向现有名称添加定义 |
supersede | 替换现有名称的定义 |
还有三个前缀类似于声明符,但作用于预定义变量
前缀 | 效果 |
---|---|
temp | 在范围结束时恢复变量的值 |
let | 如果块退出失败,则在范围结束时恢复变量的值 |
常量 | 声明容器值在其生命周期内不会更改 |
my
声明符§
使用 my
声明变量会赋予它词法作用域。这意味着它只存在于当前块中。例如
say ; # Exception! "Variable '$foo' is not declared"
这会引发错误,因为 $foo
仅在我们处于同一作用域时才被定义。
为了在同一语句中创建多个具有词法作用域的变量,请用括号括住变量
my ( , );
另请参见 声明具有词法或包作用域的变量列表。
此外,词法作用域意味着可以在新作用域中临时重新定义变量
my = "outside";sub outer-locationouter-location; # OUTPUT: «outside»sub in-buildingin-building; # OUTPUT: «inside»outer-location; # OUTPUT: «outside»
如果已重新定义变量,则引用外部变量的任何代码将继续引用外部变量。因此,在这里,&outer-location
仍打印外部 $location
sub new-locationnew-location; # OUTPUT: «outside»
要使 new-location()
打印 nowhere
,请使用 the * twigil 将 $location
设为动态变量。在尝试本地作用域后,此 twigil 会使编译器在调用作用域中查找符号,而不是在外部作用域中查找。
my
是子例程的默认作用域,因此 my sub x() {}
和 sub x() {}
执行完全相同的功能。
our
声明符§
our
变量是在周围包的作用域中创建的。它们还在词法作用域中创建别名,因此它们也可以像 my
变量一样使用。
# Available as $M::Var here.
为了同时创建多个具有包作用域的变量,请用括号括住变量
our ( , );
另请参阅声明具有词法或包范围的变量列表一节。
声明具有词法 (my
) 或包 (our
) 范围的变量列表§
一次可以对多个变量进行作用域限定,但 my
和 our
都要求将变量放入括号中
my (, , ); # same as my @a; my $s; my %h;our (, , ); # same as our @aa; our $ss; our %hh;
这可以与解构赋值结合使用。对这样的列表进行的任何赋值都将获取左侧列表中提供的元素数量,并将右侧列表中的相应值分配给它们。任何缺失的元素都将根据变量的类型导致未定义的值。
my (Str , Str , Int ) = <a b>;say [, , ].raku;# OUTPUT: «["a", "b", Int]»
要将列表解构为单个值,请使用 ($var,)
创建一个只有一个元素的列表字面量。当与变量声明符一起使用时,只需在单个变量周围提供括号即可。
sub f ;my () = f;say .raku;# OUTPUT: «1»
要跳过列表中的元素,请使用匿名状态变量 $
。
my ($,,$,) = ('a', 'b', [1,2,3], );say [, ].raku;# OUTPUT: «["b", {:th(1)}]»
has
声明符§
has
将属性作用域限定为类的实例或角色,并将方法作用域限定为类或角色。has
对于方法是隐含的,因此 has method x() {}
和 method x() {}
执行相同操作。
请参阅面向对象以获取更多文档和一些示例。
anon
声明符§
anon
声明符阻止符号安装在词法作用域、方法表和任何其他地方。
例如,你可以使用它来声明知道自己名称的子例程,但仍然不会安装在作用域中
my =half => anon sub half() ,square => anon sub square() ,;say <square>.name; # squaresay <square>(8); # 64
由于它是一个声明符,因此可以在声明任何内容的任何地方应用它,例如对于类甚至无 sigil 变量。
say anon ; # OUTPUT: «(þ)»say anon sub þ ; # OUTPUT: «&þ»
由于这些符号未安装在作用域中,因此不能按名称使用它们。但是,如果需要将它们分配给外部变量并且它们需要知道自己的名称,它们很有用,但可以使用自省来检索此名称。
my = anon class;say .new( :3bar).equal( .new( :3bar ) );# OUTPUT: «True»
state
声明符§
state
声明词法作用域变量,就像 my
一样。但是,初始化只发生一次,即在正常执行流程中首次遇到初始化时。因此,状态变量将在封闭块或例程的多次执行中保留其值。
因此,子例程
sub a;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 ();foo; # OUTPUT: «12»foo; # OUTPUT: «12»
在这种情况下,每次进入运行 for 循环的块时都会创建一个新的状态变量,这就是为什么在每次调用 foo
时状态变量都会重置。
这适用于包含代码对象的每个“克隆”,如下例所示
( xx 3).map: ; # OUTPUT: «121212»
请注意,当同一块代码的同一克隆由多个线程运行时,这不是一个线程安全结构。还要记住,方法每个类只有一个克隆,而不是每个对象都有一个克隆。
与 my
一样,多个 state
变量的声明必须放在括号中,对于单个变量,括号可以省略。
许多运算符带有隐式绑定,这可能导致远距离操作。
使用 .clone
或强制转换来创建一个可以绑定的新容器。
my ;my ;sub f();f for 1..3;say ; # OUTPUT: «[k1 => 3 k2 => 3 k3 => 3]»say ; # OUTPUT: «[k1 => 1 k2 => 2 k3 => 3]»
状态变量在所有线程之间共享。结果可能是意外的。
sub code();awaitstart ,start ;# OUTPUT: «1234435»# OUTPUT: «21345»# many other more or less odd variations can be produced
$
变量§
除了显式声明的命名状态变量之外,$
可用作匿名状态变量,而无需显式 state
声明。
say "1-a 2-b 3-c".subst(:g, /\d/, );# OUTPUT: «one-a two-b three-c»
此外,状态变量可以在子例程之外使用。例如,你可以在单行程序中使用 $
来对文件中的行进行编号。
raku -ne 'say ++$ ~ " $_"' example.txt
词法作用域中的每个对 $
的引用实际上都是一个单独的变量。
raku -e '{ say ++$; say $++ } for ^5'
# OUTPUT: «1021324354»
这就是为什么,如果你需要多次引用同一个 $
变量(或者,就此而言,任何其他匿名状态变量 @
和 %
),一种可能的解决方案是将另一个变量绑定到它,尽管在这个示例中,直接声明状态 $x
而不使用神奇/匿名 $
变量会更直接
sub foo ()foo() for ^3; # OUTPUT: «135»
通常,如果你必须多次引用一个变量,最好声明一个命名状态变量。
请注意,隐式状态声明符仅应用于变量本身,而不应用于可能包含初始化程序的表达式。如果必须完全调用一次初始化程序,则必须提供 state
声明符。
for ^3 # OUTPUT: «012»for ^3 # OUTPUT: «0»
@
变量§
类似于 $
变量,还有一个 Positional
匿名状态变量 @
。
sub foo()foo() for ^3;# OUTPUT: «[0]# [0 1]# [0 1 2]»
这里的 @
用括号括起来,以便将表达式与名为 @.push
的类成员变量区分开来。索引访问不需要这种消除歧义,但你需要复制值才能对其执行任何有用的操作。
sub foo()foo() for ^3;# OUTPUT: «[0]# [0 1]# [0 1 2]»
与 $
一样,在作用域中每次提及 @
都会引入一个新的匿名数组。
%
变量§
此外,还有一个 Associative
匿名状态变量 %
。
sub foo()foo() for ^3;# OUTPUT: «{0 => 0}# {0 => 0, 1 => 1}# {0 => 0, 1 => 1, 2 => 2}»
消除歧义的相同警告也适用。正如你所料,索引访问也是可能的(通过复制使其有用)。
sub foo()foo() for ^3;# OUTPUT: «{0 => 0}# {0 => 0, 1 => 1}# {0 => 0, 1 => 1, 2 => 2}»
与其他匿名状态变量一样,在给定作用域内每次提及 %
实际上都会引入一个单独的变量。
augment
声明符§
使用 augment
,你可以向现有类和语法添加方法,但不能添加属性,前提是你首先激活了 MONKEY-TYPING
实用程序。
由于类通常是our
作用域的,因此是全局的,这意味着修改全局状态,这是强烈不建议的。对于几乎所有情况,都有更好的解决方案。
# don't do thisuse MONKEY-TYPING;augmentsay 42.is-answer; # OUTPUT: «True»
(在这种情况下,更好的解决方案是使用函数)。
为了获得更好、更安全的示例,这是一个创建类模块以通过添加当前缺少的方法来扩展IO::Path
的实用方法,以产生在删除extension
后basename
剩余的部分。(请注意,对于该部分的名称或甚至如何构建它,没有明确的开发者共识。)
unit is IO::Path;method new(|c)use MONKEY-TYPING;augment
temp
前缀§
与my
类似,temp
在其作用域结束时恢复变量的旧值。但是,temp
不会创建新变量。
my = 0; # temp will "entangle" the global variable with the call stack# that keeps the calls at the bottom in order.sub f(*);sub g(*);print g(g(f(g()), g(), f()));# OUTPUT: «<g># <g># <f># <g># </g># </f># <g># </g># <f># </f># </g># </g>»
let
前缀§
如果块未成功退出,则恢复前一个值。成功退出意味着该块返回一个已定义的值或列表。
my = 42;say ;
在上述情况下,如果Bool.pick
返回 true,则答案将保持为 84,因为该块返回一个已定义的值(say
返回True
)。否则,die
语句将导致块未成功退出,从而将答案重置为 42。
constant
前缀§
constant
前缀声明它标记的值在其生命周期内不会改变。
constant = pi * 2;= 6; # OUTPUT: «(exit code 1) Cannot assign to an immutable value
该值在编译时分配。由于 Raku 模块是自动预编译的,因此在运行程序时不会重新评估模块中定义的常量。有关其他信息,请查看术语页面中的常量部分。
类型约束和初始化§
变量通过其绑定的容器具有类型约束,该容器位于声明符和变量名之间。默认类型约束是Mu
。您还可以使用特征of来设置类型约束。
my Int = 42;= 'a string';CATCH# OUTPUT: «X::TypeCheck::Assignment: Type check failed in assignment to $x;expected Int but got Str ("a string")»
如果标量变量具有类型约束但没有初始值,则使用其绑定的容器的默认值的类型对象对其进行初始化。
my Int ;say .^name; # OUTPUT: «Int»say .defined; # OUTPUT: «False»
没有显式类型约束的标量变量被键入为Mu
,但默认为Any
类型对象。
带有@
标记的变量用空Array
初始化;带有%
标记的变量用空Hash
初始化。
变量的默认值可以使用is default
特征设置,并通过向其分配Nil
重新应用
my Real is default(1);say ; # OUTPUT: «1»*= 5;say ; # OUTPUT: «5»= Nil;say ; # OUTPUT: «1»
默认已定义变量编译指示§
要强制所有变量具有确定性约束,请使用编译指示use variables :D
。该编译指示具有词法作用域,可以使用use variables :_
关闭。
use variables :D;my Int ;# OUTPUT: «===SORRY!=== Error while compiling <tmp>Variable definition of type Int:D (implicit :D by pragma) requires an initializer ...my Int = 1; # that works# switch it off in this block
请注意,分配Nil
会将变量还原为其默认值,该默认值通常不是确定值,因此会失败约束
use variables :D;my Int = 42;= 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> # binds $_ to 'a', 'b' and 'c' in turnsay for <a b c>; # same, even though it's not a blockgiven 'a' # binds $_ to 'a'say given 'a'; # same, even though it's not a block
因为 $_
绑定到迭代的值,所以如果 $_
绑定到可赋值的东西,你也可以赋值给 $_
。
my = ^5; # 0 through 4++ for ; # increment all elements of @numberssay ;# 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*># OUTPUT: «Looking for strings with non-alphabetic characters...# ab:c# d$e# ij*»
$/
变量§
$/
是匹配变量。在每个例程中创建一个新的变量。它被设置为最后一个 正则表达式 匹配的结果,因此通常包含 Match
类型的对象。
'abc 12' ~~ /\w+/; # sets $/ to a Match objectsay $/.Str; # OUTPUT: «abc»
Grammar.parse
方法还将调用者的 $/
设置为结果 Match
对象。对于以下代码
use XML::Grammar; # zef install XMLXML::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+ =[ + ] \s* = [ \w+ . ] /;say $/<punctuation>; # OUTPUT: «「....」»say $/<final-word>; # OUTPUT: «「see?」»
还可以通过快捷方式 $<named>
访问它们。
say ; # OUTPUT: «「....」»say ; # OUTPUT: «「see?」»
要获取所有命名属性,可以使用 $/.hash
或 %$/
。在 6.d 语言之前,你还可以使用 %()
快捷方式(括号内没有空格)。
say %$/.join; # OUTPUT: «"punctuation ....final-word see?"»# 6.c language onlysay %().join; # OUTPUT: «"punctuation ....final-word see?"»
此行为已从 6.d 版本开始弃用。
线程安全问题§
因为 $/
仅在每个例程中定义,所以当你在循环中进行匹配时,实际上是在重复使用同一个 $/
。在单线程程序中,这不是问题。但是,如果你要使用 hyper
或 race
让多个线程并行进行匹配,那么共享“外部”$/
就会成为一个问题,因为那时它正在线程之间共享!幸运的是,解决方案非常简单:在你进行匹配的范围内定义你自己的 $/
。例如,获取一个文本源,对其运行正则表达式,并使用并行执行将其映射到哈希
my = .race.map:
$!
变量§
$!
是错误变量。在每个例程中都会创建一个新的变量。如果 try
块或语句前缀捕获到异常,则该异常将存储在 $!
中。如果没有捕获到异常,则 $!
设置为 Nil
。
请注意,CATCH
块不会设置 $!
。相反,它们将块内的 $_
设置为捕获到的异常。
另请注意,与 $/
相同,$!
的使用也适用于相同的线程安全问题。
$¢
变量§
请参阅 Match
中的描述。
编译时变量§
所有编译时变量的 twigil 中都包含一个问号。由于是编译时变量,因此无法在运行时更改它们,但是它们对于内省程序非常有价值。最常见的编译时变量如下
$?FILE | 我在哪个文件中? |
$?LINE | 我在哪一行?[从 1 开始索引] |
::?CLASS | 我在哪个类中? |
%?RESOURCES | 与当前编译单元的“分发”关联的文件。 |
$?FILE
和 $?LINE
也可从 CallFrame
中分别作为 file
和 line
方法获得。
%?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 = <images/foo.jpg>; # gets an object you can slurpmy = <images/foo.jpg>.absolute; # gets an absolute path to a filemy = <images/foo.jpg>.open; # gets an opened IO::Handle to work with
请注意,已安装分发中的资源文件的路径和名称可能会被修改,因此不要在除将其用作 %?RESOURCES
变量的键之外的任何其他情况下依赖其值。
%?RESOURCES
变量未实现为普通 Hash
,而是 Distribution::Resources
类型的实例,因此不要指望通过打印或使用其他方式来检查其值来查看分发中所有可用的资源文件。相反,使用上述 API 来访问特定文件。
%?RESOURCES
变量只能在模块内访问。如果您想在模块外访问 %?RESOURCES
,您需要自己公开该 API。一种方法是在 lib
目录中创建一个例程来返回值
unit ;sub my-resources is export
然后创建一个测试文件,例如 t/resources.t
,内容如下
use Test;use MyLib;my = my-resources;isa-ok , Hash;
因此,编译时哈希的内容会公开给运行时代码。
内省编译时变量§
以下编译时变量允许更深入的内省
$?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-subawesome-sub; # OUTPUT: «awesome-sub»
它还允许递归
my = 10;sub do-workdo-work;
请注意,在 multi
中,&?ROUTINE
指的是当前候选,而不是 multi
整体。
因此,以下递归定义不起作用
multi broken-fibonacci( where * ≤ 1)multi broken-fibonacci( where * > 0)
如果调用,&?ROUTINE
将始终引用第二个 multi 候选,并且永远不会分派到第一个。如果您想对整个 proto
使用自递归,请使用函数名称或 samewith
。
&?BLOCK§
特殊编译变量 &?BLOCK
的行为类似于 &?ROUTINE
,但它允许内省一个代码块。它保存一个 Block
,并允许在同一个块内递归
for '.'
$?DISTRIBUTION§
$?DISTRIBUTION
提供对当前编译单元的 Distribution
的访问。这为模块作者提供了一种通过原始相对路径名引用 distribution 中其他文件或查看元数据(通过 .meta
方法)的方法,而无需了解底层文件结构(例如 CompUnit::Repository::Installation
在安装时如何更改文件布局)。
unit ;sub module-version
.meta
方法当前具有 ver
、auth
和 api
的哈希键,
.content
方法在提供 distribution 顶级目录的相对路径时提供对 distribution 中文件的访问。
sub module-source
请注意,content
示例的输出是一个已关闭的文件句柄。为了实际使用文件的内容,用户可以在同一模块中的另一个例程中将其提取为字符串
sub get-file-content
动态变量§
所有动态作用域变量都具有 *
twigil,并且它们的名称(按照惯例)用大写字母书写。
与参数相关的变量§
这些变量与传递给脚本的参数相关。
$*ARGFILES
§
一个 IO::ArgFiles
(IO::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
子例程预期的相同参数。
特殊文件句柄:STDIN
、STDOUT
和 STDERR
§
有关特殊文件句柄的更多信息,另请参阅 输入和输出 页面和 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
§
它包含 C
urrent W
orking D
irectory(当前工作目录)。
$*KERNEL
§
$*KERNEL
包含一个 Kernel
实例,它的 .gist
是当前运行的内核。
say ; # OUTPUT: «linux (4.4.92.31.default)»
$*DISTRO
§
此对象(类型为 Distro
)包含有关当前操作系统发行版的信息。例如
say "Some sort of Windows" if .is-win;
$*DISTRO.name
采用一组取决于操作系统的值。这些名称会因版本和实现而异,因此在程序中使用它们之前,您应该仔细检查并进行测试;由于这些名称是由实现定义的,而不是规范中的,它们可能会随时发生变化。
使用 say
显示 $*DISTRO
gist
say ; # OUTPUT: «debian (9.stretch)»
这显示了有关操作系统及其正在使用的版本的其他信息,但事实上,此变量包含对创建可移植程序有用的信息,例如路径分隔符
say .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 .precomp-ext, " ", .precomp-target; # OUTPUT: «moarvm mbc»
例如,这两个方法将显示预编译字节码脚本中使用的扩展和使用的目标。这是在 Moar 虚拟机中找到的,但它也可能因版本和实现而异。其他 VM,例如 Java,将显示不同的值。$*VM.config
包括用于创建虚拟机的所有配置值,例如
say .config<versionmajor>, ".", .config<versionminor>;# OUTPUT: «2018.11»
这是虚拟机版本,通常与解释器和整体 Raku 环境中使用的版本相同。
$*RAKU
§
Raku
类的此对象包含有关 Raku 语言当前实现的信息
say .compiler.version; # OUTPUT: «v2020.01»
但其要点包括语言名称,后跟编译器主版本
say ; # OUTPUT: «Raku (6.d)»
它字符串化为 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 MAIN
和 sub USAGE
中可用的 MAIN
子例程的签名生成的默认用法消息。该变量是只读的。
sub MAIN(, :, UInt :)
它只能在 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
对象,表示当前默认调度程序。
默认情况下,这会对使用该调度程序(例如 Promise
或 Supply
)的方法 .hyper
、.race
和其他线程池类施加最多 64 个线程的限制。但是,这取决于实现,并且可能会发生更改。要更改最大线程数,可以在运行 raku 之前设置环境变量 RAKUDO_MAX_THREADS
,或者在使用它们之前创建一个作用域副本,并更改默认值。
my = ThreadPoolScheduler.new( max_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 = FatRat;
仅针对词法作用域激活此项
my = FatRat;
它是如何工作的?$*RAT-OVERFLOW 变量应该包含一个类或实例,当 Rat 溢出时将在其上调用 UPGRADE-RAT
方法。因此,你可以通过创建一个包含 UPGRADE-RAT
方法的类来引入自己的行为。
此类方法应接受两个整数值:一个用于分子,另一个用于分母。例如(不太严肃),如果非常大,则将结果值转换为 Inf
;如果非常小,则转换为 0
$*TOLERANCE
§
由 =~=
运算符和依赖于它的任何运算使用的变量,用于决定两个值是否近似相等。默认为 1e-15
。
命名约定§
了解我们的命名约定有助于直接理解代码的作用。但是,目前还没有(也可能永远不会有)官方列表;不过,我们列出了一些广泛认可的约定。
如果可以找到一个好名称,则内置库中的子例程和方法会尝试使用单字名称。在名称由两个或更多个单词组成的情况下,它们之间用连字符分隔。
复合词被视为一个单词,因此为
substr
、subbuf
和deepmap
(就像我们在英语中写“starfish”,而不是“star fish”)。在特殊时间自动为您调用的子例程和方法都用大写字母书写。这包括
MAIN
子例程、AT-POS
和用于实现容器类型的相关方法,以及BUILD
和DESTROY
。类型名称采用驼峰式大小写,但本机类型除外,本机类型采用小写字母。对于例外情况,您可以通过以下方式记住:它们以更紧凑的方式存储,因此它们的名称看起来也更小。
内置动态变量和编译时变量始终采用大写字母,例如
$*OUT
、$?FILE
。来自 MOP 和其他内部结构的方法使用 "_" 来分隔多个单词,例如
add_method
。