Int§

Int 类型提供任意大小的整数。它们可以大到你的计算机内存允许的程度,尽管一些实现选择在要求生成真正庞大大小的整数时抛出数值溢出错误。

say 10**600**600
# OUTPUT: «Numeric overflow␤» 

与一些语言不同,当两个操作数都是 Int 类型时,使用 / 运算符执行的除法会产生一个分数,不会进行任何舍入。

say 4/5# OUTPUT: «0.8␤» 

此除法产生的类型是 RatNum 类型。如果在约简后,分数的分母小于 64 位,则会生成 Rat,否则会生成 Num 类型。

如果你希望尽可能得到一个 Int 结果,divnarrow 函数可能会有所帮助。div 运算符执行整数除法,丢弃余数,而 narrow 将数字拟合到它能拟合的最窄类型。

say 5 div 2# OUTPUT: «2␤» 
 
# Result `2` is narrow enough to be an Int: 
say (4/2).narrow# OUTPUT: «2␤» 
say (4/2).narrow.^name# OUTPUT: «Int␤» 
 
# But 2.5 has fractional part, so it ends up being a Rat type: 
say (5/2).narrow.^name# OUTPUT: «Rat␤» 
say (5/2).narrow;       # OUTPUT: «2.5␤» 
 
# Denominator is too big for a Rat, so a Num is produced: 
say 1 / 10⁹⁹; # OUTPUT: «1e-99␤» 

Raku 有一个 FatRat 类型,它提供任意精度的分数。为什么在上面的最后一个例子中,会生成一个有限精度的 Num 类型而不是 FatRat 类型?原因是:性能。大多数操作对精度损失一点是可以接受的,因此不需要使用更昂贵的 FatRat 类型。如果你希望获得额外的精度,你需要自己实例化一个。

Num§

Num 类型提供双精度浮点小数,在其他语言中有时称为“双精度数”。

Num 字面量用字母 e 分隔指数来编写。请记住,即使指数为零,字母 e 也是**必需的**,否则你会得到一个 Rat 有理数字面量。

say 42e0.^name# OUTPUT: «Num␤» 
say 42.0.^name# OUTPUT: «Rat␤» 

区分大小写的单词 InfNaN 分别表示特殊值无穷大和非数字。可以使用 U+221E INFINITY () 字符代替 Inf

Raku 尽可能遵循 IEEE 754-2008 浮点运算标准,计划在以后的语言版本中实现更多的一致性。该语言保证为任何给定的 Num 字面量选择最接近的可表示数字,并且确实提供对负零和非规格化数(也称为“次规格化数”)的支持。

请记住,像 sayput 这样的输出函数不会非常努力地区分 Numeric 类型的输出方式,并且可能会选择将 Num 显示为 IntRat 数字。为了获得更明确的输出字符串,请使用 raku 方法。

say  1e0;      # OUTPUT: «1␤» 
say .5e0;      # OUTPUT: «0.5␤» 
say  1e0.raku# OUTPUT: «1e0␤» 
say .5e0.raku# OUTPUT: «0.5e0␤» 

Complex§

Complex 类型表示复平面的复数。Complex 对象由两个 Num 对象组成,分别表示复数的实部和虚部。

要创建一个 Complex,你可以在任何其他非复数上使用 i 后缀运算符,可以选择用加法设置实部。要在 NaNInf 字面量上使用 i 运算符,请用反斜杠将其与它们分开。

say 42i;      # OUTPUT: «0+42i␤» 
say 73+42i;   # OUTPUT: «73+42i␤» 
say 73+Inf\i# OUTPUT: «73+Inf\i␤» 

请记住,上面的语法只是一个加法表达式,优先级规则适用。它也不能用于禁止表达式的场合,例如函数参数中的字面量。

# Precedence of `*` is higher than that of `+` 
say 2 * 73+10i; # OUTPUT: «146+10i␤» 

为了避免这些问题,你可以选择使用 Complex 字面量语法,它涉及用尖括号括起实部和虚部,没有任何空格

say 2 * <73+10i># OUTPUT: «146+20i␤» 
 
multi how-is-it (<2+4i>{ say "that's my favorite number!" }
multi how-is-it (|)      { say "meh"                        }
how-is-it 2+4i;  # OUTPUT: «that's my favorite number!␤» 
how-is-it 3+2i;  # OUTPUT: «meh␤» 

Rational§

实现 Rational 角色的类型提供高精度和任意精度的十进制数。由于精度越高,性能损失就越大,因此 Rational 类型有两种形式:RatFatRatRat 是最常用的变体,在大多数情况下会降级为 Num,当它不再能够保存所有请求的精度时。FatRat 是任意精度的变体,它会不断增长以提供所有请求的精度。

Rat§

最常见的 Rational 类型。它支持分母最大为 64 位的有理数(在将分数约简到最简分母后)。可以创建分母更大的 Rat 对象,但是,当分母如此大的 Rat 是数学运算的结果时,它们会降级为 Num 对象。

Rat 字面量使用类似于 Num 字面量的语法,在许多其他语言中,使用点来表示数字是小数

say .1 + .2 == .3# OUTPUT: «True␤» 

如果您尝试在许多常见语言中执行类似于上面的语句,您将得到 False 作为答案,这是由于浮点数学的不精确性。要在 Raku 中获得相同的结果,您必须使用 Num 字面量。

say .1e0 + .2e0 == .3e0# OUTPUT: «False␤» 

您还可以使用 / 除法运算符IntRat 对象一起生成 Rat

say 3/4;     # OUTPUT: «0.75␤» 
say 3/4.2;   # OUTPUT: «0.714286␤» 
say 1.1/4.2# OUTPUT: «0.261905␤» 

请记住,上面的语法只是一个除法表达式,优先级规则适用。它也不能在禁止表达式的场合使用,例如例程参数中的字面量。

# Precedence of power operators is higher than division 
say 3/2²; # OUTPUT: «0.75␤» 

为了避免这些问题,您可以选择使用 Rational 字面量语法,这涉及用尖括号将分子和分母括起来,没有任何空格

say <3/2>²; # OUTPUT: «2.25␤» 
 
multi how-is-it (<3/2>{ say "that's my favorite number!" }
multi how-is-it (|)     { say "meh"                        }
how-is-it 3/2;  # OUTPUT: «that's my favorite number!␤» 
how-is-it 1/3;  # OUTPUT: «meh␤» 

最后,任何具有属性 No 的 Unicode 字符,表示分数,都可以用作 Rat 字面量

say ½ + ⅓ + ⅝ + ⅙; # OUTPUT: «1.625␤» 

降级为 Num§

如果产生 Rat 答案的数学运算会产生分母大于 64 位的 Rat,则该运算将返回 Num 对象。但是,当构造 Rat(即它不是某个数学表达式的结果时),可以使用更大的分母

my $a = 1 / (2⁶⁴ - 1);
say $a;                   # OUTPUT: «0.000000000000000000054␤» 
say $a.^name;             # OUTPUT: «Rat␤» 
say $a.nude;              # OUTPUT: «(1 18446744073709551615)␤» 
 
my $b = 1 / 2⁶⁴;
say $b;                   # OUTPUT: «5.421010862427522e-20␤» 
say $b.^name;             # OUTPUT: «Num␤» 
 
my $c = Rat.new(12⁶⁴);
say $c;                   # OUTPUT: «0.000000000000000000054␤» 
say $c.^name;             # OUTPUT: «Rat␤» 
say $c.nude;              # OUTPUT: «(1 18446744073709551616)␤» 
say $c.Num;               # OUTPUT: «5.421010862427522e-20␤»

FatRat§

最后一个 Rational 类型——FatRat——保留您要求的所有精度,将分子和分母存储为两个 Int 对象。一个 FatRat 比一个 Rat 更具传染性,因此许多与 FatRat 的数学运算将产生另一个 FatRat,保留所有可用的精度。在 Rat 降级为 Num 的地方,使用 FatRat 的数学运算会继续进行

say ((42 + Rat.new(1,2))/999999999999999999).^name;         # OUTPUT: «Rat␤» 
say ((42 + Rat.new(1,2))/9999999999999999999).^name;        # OUTPUT: «Num␤» 
say ((42 + FatRat.new(1,2))/999999999999999999).^name;      # OUTPUT: «FatRat␤» 
say ((42 + FatRat.new(1,2))/99999999999999999999999).^name# OUTPUT: «FatRat␤» 

没有用于构造 FatRat 对象的特殊运算符或语法。只需使用 FatRat.new 方法,将分子作为第一个位置参数,将分母作为第二个参数。

如果您的程序需要大量创建 FatRat,您可以创建自己的自定义运算符

sub infix:<🙼> { FatRat.new: $^a$^b }
say (1🙼3).raku# OUTPUT: «FatRat.new(1, 3)␤» 

打印有理数§

请记住,像 sayput 这样的输出函数不会非常努力地区分 Numeric 类型的输出方式,并且可能会选择将 Num 显示为 IntRat 数字。为了获得更明确的输出字符串,请使用 raku 方法。

say 1.0;        # OUTPUT: «1␤» 
say ⅓;          # OUTPUT: «0.333333␤» 
say 1.0.raku;   # OUTPUT: «1.0␤» 
say ⅓.raku;     # OUTPUT: «<1/3>␤» 

要获得更多信息,您可以选择在 nude 中查看 Rational 对象,显示其numerator 和denominator

say ⅓;          # OUTPUT: «0.333333␤» 
say 4/2;        # OUTPUT: «2␤» 
say ⅓.raku;     # OUTPUT: «<1/3>␤» 
say <4/2>.nude# OUTPUT: «(2 1)␤» 

除以零§

在许多语言中,除以零会导致立即异常。在 Raku 中,发生的情况取决于您除以什么以及如何使用结果。

Raku 遵循 IEEE 754-2008 浮点运算标准,但出于历史原因,6.c 和 6.d 语言版本并未完全遵守。 Num 除以零会产生 Failure,而 Complex 除以零会产生 NaN 分量,无论分子是什么。

从 6.e 版本语言开始,NumComplex 除以零将分别产生 -Inf+InfNaN,具体取决于分子是负数、正数还是零(对于 Complex,实部和虚部都是 Num,并分别考虑)。

Int 数值的除法会产生一个 Rat 对象(或者一个 Num,如果在约分后分母大于 64 位,这种情况在除以零时不会发生)。这意味着这种除法永远不会产生 ExceptionFailure。结果是一个零分母有理数,它可能是爆炸性的。

零分母有理数§

零分母有理数是一个扮演 Rational 角色的数值,在核心数值中,它将是 RatFatRat 对象,其分母为零。这种有理数的分子被归一化为 -101,具体取决于原始分子是负数、零还是正数。

可以在不进行实际除法的情况下执行的操作是非爆炸性的。例如,您可以分别检查 分子分母裸值,或者执行数学运算,而不会出现任何异常或失败。

将零分母有理数转换为 Num 遵循 IEEE 约定,结果将是 -InfInfNaN,具体取决于分子是负数、正数还是零。反过来也是如此:将 ±Inf/NaN 转换为其中一种 Rational 类型将产生一个具有适当分子的零分母有理数。

say  <1/0>.Num;   # OUTPUT: «Inf␤» 
say <-1/0>.Num;   # OUTPUT: «-Inf␤» 
say  <0/0>.Num;   # OUTPUT: «NaN␤» 
say Inf.Rat.nude# OUTPUT: «(1 0)␤» 

所有其他需要对分子和分母进行非 IEEE 除法的操作将导致抛出 X::Numeric::DivideByZero 异常。最常见的此类操作可能是尝试打印或将零分母有理数转换为字符串。

say 0/0;
# OUTPUT: 
# Attempt to divide by zero using div 
#  in block <unit> at -e line 1 

同形异义词§

同形异义词 是两种类型的子类,可以表现为其中任何一种。例如,同形异义词 IntStrIntStr 类型的子类,将被任何需要 IntStr 对象的类型约束接受。

同形异义词可以使用 尖括号 创建,可以单独使用,也可以作为哈希键查找的一部分使用;直接使用 .new 方法,也由一些结构提供,例如 sub MAIN 的参数。

say <42>.^name;                 # OUTPUT: «IntStr␤» 
say <42e0>.^name;               # OUTPUT: «NumStr␤» 
say < 42+42i>.^name;            # OUTPUT: «ComplexStr␤» 
say < 1/2>.^name;               # OUTPUT: «RatStr␤» 
say <0.5>.^name;                # OUTPUT: «RatStr␤» 
 
@*ARGS = "42";
sub MAIN($x{ say $x.^name }   # OUTPUT: «IntStr␤» 
 
say IntStr.new(42"42").^name# OUTPUT: «IntStr␤» 

上面的一些结构在开头的尖括号后面有一个空格。这个空格不是偶然的。通常使用运算符编写的数值,例如 1/2 (Rat,除法运算符) 和 1+2i (Complex,加法),可以写成不涉及运算符的字面量:尖括号没有在尖括号和内部字符之间留任何空格。通过在尖括号内添加空格,我们告诉编译器,我们不仅想要一个 RatComplex 字面量,而且还希望它是一个同形异义词:在本例中是 RatStrComplexStr

如果数值字面量不使用任何运算符,那么在尖括号内编写它,即使不包含任何空格,也会产生同形异义词。(逻辑:如果你不想要同形异义词,你就不会使用尖括号。对于使用运算符的数字来说,情况并非如此,因为一些结构,例如签名字面量,不允许你使用运算符,所以你不能仅仅为了这些数值字面量而省略尖括号)。

可用的同形异义词§

核心语言提供以下同形异义词

类型同形异义词示例
IntStrInt 和 Str<42>
NumStrNum 和 Str<42e0>
ComplexStrComplex 和 Str< 1+2i>
RatStrRat 和 Str<1.5>

注意:没有 FatRatStr 类型。

同形词的强制转换§

请记住,同形词只是它们所代表类型的子类。就像一个类型约束为 Foo 的变量或参数可以接受 Foo 的任何子类一样,一个类型约束为 Int 的变量或参数也会接受 IntStr 同形词。

sub foo(Int $x{ say $x.^name }
foo <42>;                          # OUTPUT: «IntStr␤» 
my Num $y = <42e0>;
say $y.^name;                      # OUTPUT: «NumStr␤» 

这也适用于参数 强制转换器

sub foo(Int(Cool$x{ say $x.^name }
foo <42>;  # OUTPUT: «IntStr␤» 

给定的同形词已经是 Int 类型的对象,因此在这种情况下它不会被转换为“普通” Int

如果无法将同形词“折叠”为其组件之一,那么同形词的功能将大大降低。因此,如果您显式调用一个以要强制转换的类型命名的方法,您将只获得该组件。同样适用于任何代理方法,例如调用方法 .Numeric 而不是 .Int,或者使用 prefix:<~> 运算符 而不是 .Str 方法调用。

my $al := IntStr.new: 42"forty two";
say $al.Str;  # OUTPUT: «forty two␤» 
say +$al;     # OUTPUT: «42␤» 
 
say <1/99999999999999999999>.Rat.^name;    # OUTPUT: «Rat␤» 
say <1/99999999999999999999>.FatRat.^name# OUTPUT: «FatRat␤» 

强制转换一整列同形词的一种便捷方法是将 超运算符 应用于适当的前缀。

say map *.^name,   <42 50e0 100>;  # OUTPUT: «(IntStr NumStr IntStr)␤» 
say map *.^name+«<42 50e0 100>;  # OUTPUT: «(Int Num Int)␤» 
say map *.^name~«<42 50e0 100>;  # OUTPUT: «(Str Str Str)␤» 

对象标识§

当我们考虑对象标识时,上面关于强制转换同形词的讨论变得更加重要。一些构造使用它来确定两个对象是否“相同”。虽然对人类来说,同形词 42 和普通 42 可能看起来“相同”,但对这些构造来说,它们是完全不同的对象。

# "42" shows up twice in the result: 42 and <42> are different objects: 
say unique 11142, <42># OUTPUT: «(1 42 42)␤» 
# Use a different operator to `unique` with: 
say unique :with(&[==]), 11142, <42># OUTPUT: «(1 42)␤» 
# Or coerce the input instead (faster than using a different `unique` operator): 
say unique :as(*.Int), 11142, <42># OUTPUT: «(1 42)␤» 
say unique +«(11142, <42>);         # OUTPUT: «(1 42)␤» 
 
# Parameterized Hash with `Any` keys does not stringify them; our key is of type `Int`: 
my %h{Any} = 42 => "foo";
# But we use the allomorphic key of type `IntStr`, which is not in the Hash: 
say %h<42>:exists;           # OUTPUT: «False␤» 
# Must use curly braces to avoid the allomorph: 
say %h{42}:exists;           # OUTPUT: «True␤» 
 
# We are using a set operator to look up an `Int` object in a list of `IntStr` objects: 
say 42  <42 100 200># OUTPUT: «False␤» 
# Convert it to an allomorph: 
say <42>  <42 100 200># OUTPUT: «True␤» 
# Or convert the items in the list to plain `Int` objects: 
say 42  +«<42 100 200># OUTPUT: «True␤» 

请注意这些对象标识差异,并根据需要强制转换您的同形词。

转换非同形词§

当我们显式地将变量创建为 StrNumeric 类型时,它们不是 Allomorph

my $a = "010"say $a.^name# OUTPUT: «Str␤» 
my $b = 42;    say $b.^name# OUTPUT: «Int␤» 

然后我们可以显式地将它们转换为它们的 Allomorph 对应物。

$a .= Numeric# OUTPUT: «10␤» 
say $a.^name;  # OUTPUT: «Int␤» 
$b .= Str;   ; # OUTPUT: «42␤» 
say $b.^name;  # OUTPUT: «Str␤» 

或者我们可以自然地将它们强制转换为其他形式。

my $a = "010"say $a.^name# OUTPUT: «Str␤» 
my $b = $a + 1;              # OUTPUT: «11␤» 
say $b.^name;                # OUTPUT: «Int␤» 

原生数字§

顾名思义,原生数字提供对原生数字的访问——即您的硬件直接提供的数字。这反过来提供了两个特性:溢出/下溢和更好的性能。

注意:在撰写本文时(2018.05),某些实现(例如 Rakudo)对原生类型提供了一些零星的细节,例如 int64 是否可用以及在 32 位机器上是否为 64 位大小,以及如何在您的程序在这样的硬件上运行时检测到这一点。

可用的原生数字§

原生类型基本数字大小
int整数64 位
int8整数8 位
int16整数16 位
int32整数32 位
int64整数64 位
uint无符号整数64 位
uint8无符号整数8 位
uint16无符号整数16 位
uint32无符号整数32 位
uint64无符号整数64 位
num浮点数64 位
num32浮点数32 位
num64浮点数64 位
atomicint整数大小适合提供 CPU 提供的原子操作。(通常在 64 位平台上为 64 位,在 32 位平台上为 32 位)

创建原生数字§

要创建原生类型的变量或参数,只需使用可用数字之一的名称作为类型约束。

my int32 $x = 42;
sub foo(num $y{}
class { has int8 $.z }

有时,您可能希望将某个值强制转换为原生类型,而无需创建任何可用的变量。没有 .int 或类似的强制转换方法(方法调用是延迟绑定的,因此它们不适合此目的)。相反,只需使用匿名变量。

some-native-taking-sub( (my int $ = $y), (my int32 $ = $z) )

溢出/下溢§

尝试赋值一个不适合特定原生类型的值,会产生异常。这包括尝试向原生参数提供过大的参数。

my int $x = 2¹⁰⁰;
# OUTPUT: 
# Cannot unbox 101 bit wide bigint into native integer 
#  in block <unit> at -e line 1 
 
sub f(int $x{ $x }say f 2⁶⁴
# OUTPUT: 
# Cannot unbox 65 bit wide bigint into native integer 
#   in sub f at -e line 1 
#   in block <unit> at -e line 1 

然而,修改现有值使其变得过大或过小,会导致溢出或下溢行为。

my int $x = 2⁶³-1;
say $x;             # OUTPUT: «9223372036854775807␤» 
say ++$x;           # OUTPUT: «-9223372036854775808␤» 
 
my uint8 $x;
say $x;             # OUTPUT: «0␤» 
say $x -= 100;      # OUTPUT: «156␤» 

创建使用原生类型的对象不会涉及程序员的直接赋值;这就是为什么这些结构提供溢出/下溢行为而不是抛出异常。

say Buf.new(100020003000).List# OUTPUT: «(232 208 184)␤» 
say my uint8 @a = 100020003000# OUTPUT: «232 208 184␤» 

自动装箱§

虽然它们可以被称为“原生类型”,但原生数值实际上并不是具有任何方法的类。但是,您可以调用这些数值的非原生版本上可用的任何方法。这是怎么回事?

my int8 $x = -42;
say $x.abs# OUTPUT: «42␤» 

这种行为被称为“自动装箱”。编译器会自动将原生类型“装箱”到一个具有所有方法的全功能高级类型中。换句话说,上面的int8被自动转换为Int,然后是Int类提供了被调用的abs方法。

当您使用原生类型来提高性能时,这个细节很重要。如果您使用的代码导致执行了大量的自动装箱,那么使用原生类型可能会比使用非原生类型获得更差的性能。

my $a = -42;
my int $a-native = -42;
{ for ^1000_000 { $a.abs        }say now - ENTER now } # OUTPUT: «0.38180862␤» 
{ for ^1000_000 { $a-native.abs }say now - ENTER now } # OUTPUT: «0.938720␤» 

如上所示,原生变体速度慢了一倍多。原因是方法调用需要将原生类型装箱,而非原生变体不需要这样做,因此导致性能下降。

在这种特殊情况下,我们可以简单地切换到abs的子程序形式,它可以与原生类型一起使用而无需装箱它们。在其他情况下,您可能需要寻求其他解决方案来避免过度自动装箱,包括将代码的一部分切换到非原生类型。

my $a = -42;
my int $a-native = -42;
{ for ^1000_000 { abs $a        }say now - ENTER now } # OUTPUT: «0.38229177␤» 
{ for ^1000_000 { abs $a-native }say now - ENTER now } # OUTPUT: «0.3088305␤» 

默认值§

由于原生类型后面没有类,因此没有您通常在未初始化的变量中获得的类型对象。因此,原生类型会自动初始化为零。在 6.c 语言中,原生浮点类型(numnum32num64)被初始化为值NaN;在 6.d 语言中,默认值为0e0

原生分派§

可以将原生候选者与非原生候选者一起使用,例如,当大小可预测时,使用原生候选者提供更快的算法,但在其他情况下则回退到较慢的非原生备选方案。以下是在涉及原生候选者的多重分派方面的规则。

首先,原生类型的尺寸在分派中不起作用,int8被认为与int16int相同。

multi foo(int   $x{ say "int" }
multi foo(int32 $x{ say "int32" }
foo my int $x = 42;
# OUTPUT: 
# Ambiguous call to 'foo(Int)'; these signatures all match: 
# :(int $x) 
# :(int32 $x) 

其次,如果一个例程是only——即它不是一个multi——它接受一个非原生类型,但在调用期间给出了一个原生类型,反之亦然,那么参数将被自动装箱或自动拆箱以使调用成为可能。如果给定的参数太大而无法放入原生参数中,则会抛出异常。

-> int {}42 );            # OK; auto-unboxing 
-> int {}2¹⁰⁰ );          # Too large; exception 
-> Int {}2¹⁰⁰ );          # OK; non-native parameter 
-> Int {}my int $ = 42 ); # OK; auto-boxing 

当涉及到multi例程时,如果不存在原生候选者来接受原生参数,则原生参数将始终被自动装箱。

multi foo (Int $x{ $x }
say foo my int $ = 42# OUTPUT: «42␤» 

当以相反的方式进行时,不会提供相同的便利。如果只有一个原生候选者可用,则非原生参数不会被自动拆箱,而是会抛出一个指示没有候选者匹配的异常(这种不对称的原因是原生类型始终可以被装箱,但非原生类型可能太大而无法放入原生类型中)。

multi f(int $x{ $x }
my $x = 2;
say f $x;
# OUTPUT: 
# Cannot resolve caller f(Int); none of these signatures match: 
#     (int $x) 
#   in block <unit> at -e line 1 

但是,如果调用中有一个参数是原生类型,而另一个参数是数字字面量,则此规则将被放弃。

multi f(intint{}
f 42my int $x# Successful call 

这样您就不必不断地编写,例如,$n +> 2 作为 $n +> (my int $ = 2)。编译器知道字面量足够小以适合原生类型,并将其转换为原生类型。

原子操作§

该语言提供了一些操作,这些操作保证以原子方式执行,即可以安全地由多个线程执行,而无需锁定,并且没有数据竞争的风险。

对于此类操作,需要使用 atomicint 原生类型。此类型类似于普通的原生 int,但它的大小经过调整,以便可以对其执行 CPU 提供的原子操作。在 32 位 CPU 上,它通常大小为 32 位,而在 64 位 CPU 上,它通常大小为 64 位。

# !!WRONG!! Might be non-atomic on some systems 
my int $x;
await ^100 .map: { start $x++ };
say $x# OUTPUT: «98␤» 
 
# RIGHT! The use of `atomicint` type guarantees operation is atomic 
my atomicint $x;
await ^100 .map: { start $x++ };
say $x# OUTPUT: «100␤» 

int 的相似性也存在于多重分派中:atomicint、普通 int 和大小为 int 的变体都被分派器视为相同,无法通过多重分派进行区分。

数值传染性§

数值“传染性”决定了当两个不同类型的数值参与某些数学运算时,结果的类型。如果结果是该类型的而不是另一个操作数的类型,则称该类型比另一个类型更具传染性。例如,Num 类型比 Int 更具传染性,因此我们可以预期 42e0 + 42 会产生一个 Num 作为结果。

传染性如下所示,最具传染性的类型列在最前面

  • Complex

  • Num

  • FatRat

  • Rat

  • Int

say (2 + 2e0).^name# Int + Num => OUTPUT: «Num␤» 
say (½ + ½).^name# Rat + Rat => OUTPUT: «Rat␤» 
say (FatRat.new(1,2+ ½).^name# FatRat + Rat => OUTPUT: «FatRat␤» 

同形词与其数值组件具有相同的传染性。原生类型会自动装箱,并与其装箱变体具有相同的传染性。