通用§

Raku、Rakudo 和 Perl 6 之间有什么区别?§

严格来说,Rakudo 是 Raku 的一种实现。它目前正在开发中,但过去也存在其他实现,将来也可能会出现其他实现。Raku 是语言的定义。在谈论当前的解释器时,Rakudo 和 Raku 可以互换使用。“Perl 6”是 2019 年 10 月之前用于“Raku”的名称。

Raku 什么时候发布的?§

Rakudo 2015.12 实现版本于 2015 年 12 月 25 日发布,它实现了 v6.c Raku 规范,该规范于同一天发布。

有 Raku 版本 6.0.0 吗?§

没有。第一个稳定的语言规范版本是 v6.c(“圣诞节”)。规范的未来版本可能会有小版本发布(例如,v6.d.2)或主要版本发布(例如,v6.e)。

运行 raku -v 将显示您的编译器实现的语言版本,例如:

$ raku -v
Welcome to Rakudo™ v2022.04.
Implementing the Raku® Programming Language v6.d.
Built on MoarVM version 2022.04.

v6.d 版本什么时候发布的?§

v6.d 规范于 2018 年的排灯节 发布,即 2018 年 11 月 6 日至 7 日,在一个方便的时区。6.d 在 2018.11 年的 Rakudo 编译器版本中默认启用。

绝大多数 6.d 功能已经在 Rakudo 编译器中实现并可用,无需任何特殊 pragma,因为它们不会与 6.c 规范冲突。如果您在文件顶部有 use v6.d pragma,则一小部分功能和行为会自动可用。语言规范中大约 3100 个新提交的其余部分只是澄清了以前未定义的行为。

作为一名 Raku 用户,我应该安装什么?§

Mac 用户可以使用最新版本的 Rakudo Star DMG 二进制安装程序,地址为 https://rakudo.perl5.cn/downloads/star

Windows 用户可以使用 Rakudo Star MSI 二进制安装程序。您需要 Windows Git 和 Strawberry Perl 来使用 zef 安装库模块。

Linux 用户可能希望下载 Rakudo Star 并按照 https://www.raku.org/downloads/ 上的编译说明进行操作。

应该有来自供应商和第三方提供的 Linux 和 Mac 二进制文件,尽管供应商版本可能已过时。应避免使用 2015.12 年之前的 Rakudo 版本。

有一个官方的 Rakudo Star docker 镜像,地址为 https://hub.docker.com/_/rakudo-star/

作为一名高级用户,我想跟踪 Rakudo 的开发。§

一种选择是克隆 存储库 并构建它。这将安装正在进行的工作,这些工作经过了最少的测试,可能包含严重错误。如果您有兴趣为 Rakudo Raku 编译器做出贡献,您可能会发现 Z-Script 辅助工具 有用。

要安装最新的官方每月发布版本,请查看 https://raw.githubusercontent.com/rakudo/rakudo/master/VERSION 上可见的标签,或设置 辅助命令

一些用户选择使用 rakubrew,它允许快速并行安装多个版本的 Rakudo。

无论哪种情况,您可能还需要从 生态系统 中安装 zefrakudoc

在哪里可以找到关于 Raku 的良好文档?§

请参阅 官方文档网站(尤其是其 “简介”部分)以及 资源页面。您还可以参考这个 很棒的速查表

perl6book.com 包含纸质书和电子书的列表。

阅读第三方文章时请注意出版日期。2015 年 12 月之前发布的任何内容都可能描述的是 Raku 的预发布版本。

您始终可以在 我们的帮助聊天中获得来自真人帮助,或者 搜索聊天记录 以查找以前的对话和讨论。

我可以获得一些关于 Raku 的书籍吗?§

以下是一些可用的书籍,按字母顺序排列

已出版或正在出版的书籍列表在 raku.org 中维护。

什么是 Raku 规范?§

规范是指 Raku 的官方测试套件。它被称为 roast,并托管在 github 上。任何通过测试的编译器都被认为实现了该版本的 Raku 规范。

Roast 的 master 分支对应于最新的开发,这些开发不一定属于任何规范。其他分支对应于特定版本;例如,“6.c-errata”。

所以 6.c-errata 是一个已发布的语言版本,我们不会更改它,除非修复测试中的错误(“勘误”),而 master 是未发布的正在进行的工作,可能会成为下一个语言版本。它的当前状态不一定规定下一个语言版本的行为,因为新添加的功能将被审查是否包含在发布版本中。

是的,请参见 词汇表

我是一个 Perl 程序员。在哪里可以找到 Perl 和 Raku 之间的差异列表?§

文档的介绍部分 中有几个 Perl 到 Raku 的指南,其中最值得注意的是 概述

我是一个 Ruby 程序员,正在寻找快速入门类型的文档?§

请参见 rb-nutshell 指南。

模块§

是否有 Raku 的第三方库模块的存储库?§

是的。作为用户,zef 模块安装程序 会自动安装版本号最高的模块版本。如果您想要特定版本和/或来自特定作者的版本,您也可以使用 zef 指定。

作为新的模块作者,您可以使用 fez 模块上传器 将您的模块上传到 Raku 生态系统。还有一些辅助模块可以帮助您设置分发的骨架,例如 App::Mi6,它也会在您的模块准备好分发后帮助您上传。

从历史上看,您也可以使用 PAUSE 将 Raku 模块上传到 CPAN,以上传模块。在此之前,有一种使用 Github/Gitlab 使您的模块可供下载的方法。这些现在被认为不是新模块作者开始的最佳方式。

是否有 Raku 的 perldoc(命令行文档查看器)?§

是的,它被称为 rakudoc,并且以该名称存在于生态系统中。它与 Rakudo Star 捆绑在一起,但如果您使用的是 Rakudo 月度发布版,则需要使用 zef 手动安装。

我可以在 Raku 中使用 Perl 模块吗?§

是的,使用 Inline::Perl5,它与大多数 Perl 模块配合良好。它甚至可以运行 Perl Catalyst 和 DBI。

我可以在 Raku 中使用 C 和 C++ 吗?§

Nativecall 使这变得特别容易。

Nativecall 找不到 libfoo.so,而我只有 libfoo.so.1.2§

在大多数 Linux 系统中,共享库的安装方式是,对于特定的 libfoo,将有一个 libfoo.so.x.y.z 真实文件,然后是一组符号链接 libfoo.solibfoo.so.x。例如,ls /usr/local/lib/libxxhash.so* 返回

/usr/local/lib/libxxhash.so -> libxxhash.so.0.6.5
/usr/local/lib/libxxhash.so.0 -> libxxhash.so.0.6.5
/usr/local/lib/libxxhash.so.0.6.5

通常,在 Linux 中安装 libfoo-devlibfoo-devel(取决于发行版)将安装共享库 *并* 为您设置这些符号链接。但在某些情况下,您将只有,如问题中所示,libfoo.so.1.2

在这种情况下,只需使用显式设置 ABI/API 版本的 is native 版本,如 手册 中所述

sub call-foo() is native('foo',v1.2);

所有传统的 UNIX 库函数都去哪里了?§

使用 NativeCall 访问它们非常容易。

生态系统模块 POSIX 也可用。

Rakudo 有核心标准库吗?§

Rakudo Star 发行版 确实附带了 许多有用的模块

Rakudo 编译器专用版本只包含 几个最基本的模块

更多模块可以在 生态系统 中找到。

是否有类似 B::Deparse 的东西/如何获取 AST?§

使用 --target=optimize 命令行选项查看程序的 AST,例如:raku --target=optimize -e 'say "hi"'

目标 optimize 在静态优化器完成其工作后提供 AST,而目标 ast 在该步骤之前提供 AST。要获取可用目标的完整列表,请运行 raku --stagestats -e ""

什么是预编译?§

当您第一次加载模块时,Rakudo 会将其编译成字节码。然后,Rakudo 会将编译后的字节码存储在磁盘上并使用它,因为这样做往往会快得多。

模块之间可以有循环依赖吗?§

不可以,您不能有循环依赖,如果模块之间存在循环依赖,您应该会收到 Circular module loading detected 错误。

您很可能可以使用 角色 来完成您想要做的事情。与其让 A.rakumod 依赖于 B.rakumod,而 B.rakumod 依赖于 A.rakumod,不如让 A-Role.rakumodB-Role.rakumod 存在,并在 A.rakumodB.rakumod 中的类分别实现这些角色。然后,您可以依赖于 A-Role.rakumodB-Role.rakumod,而无需循环依赖。

循环依赖在 Raku 中不起作用的原因之一是单遍解析。当我们解析 B 时,我们必须知道 A 的含义,当我们解析 A 时,我们必须知道 B 的含义,这显然是一个无限循环。

请注意,Raku 没有“1 个文件 = 1 个类”的限制,并且单个编译单元(例如文件)内的循环依赖可以通过存根实现。因此,另一种可能的解决方案是将类移动到同一个编译单元中。

常见操作§

字符串:如何解析并从 字符串 中获取 数字§

使用 + 前缀

say "42.123456789123456789";  # OUTPUT: «42.123456789123456789␤» 
say +"42.4e-2";               # OUTPUT: «0.424␤» 

这个 上下文化 的例子可以将任何可以作为 字面量数字 输入的字符串转换为数字。 val 例程将其转换为 同形异义词unival 例程转换一个 Unicode 代码点。

字符串:如何检查字符串是否包含子字符串,如果是,如何获取匹配的索引?§

使用 .contains.indices

"az and az and az again".contains("az"); # OUTPUT: «True␤» 
"az and az and az again".indices("az");  # OUTPUT: «(0 7 14)␤» 

字符串:如何获取字符串的十六进制表示?§

要获取字符串中每个字节的十六进制列表(即十六进制编码器),首先使用 .encode 将字符串转换为 Blob

say "I ❤ 🦋".encode>>.base(16);  # OUTPUT: «(49 20 E2 9D A4 20 F0 9F A6 8B)␤» 

请注意,.gist.raku 方法对于变量自省很有用

say "I ❤ 🦋".encode.raku;  # OUTPUT: «utf8.new(73,32,226,157,164,32,240,159,166,139)␤» 
say "I ❤ 🦋".encode.gist;  # OUTPUT: «utf8:0x<49 20 E2 9D A4 20 F0 9F A6 8B>␤» 

字符串:如何根据索引从字符串中删除一些字符?§

使用 .comb 将其转换为 Seq,然后使用 (-) 中缀 删除不需要的索引

say '0123456789'.comb[(^* (-) (1..38).flat).keys.sort].join;  # OUTPUT: «045679␤» 

如果字符串很大,.comb 可能需要时间。在这种情况下,.substr-rw 更快

multi postcircumfix:<[- ]> (Str:D $str is copy+@indices{
    for @indices.reverse {
        when Int   { $str.substr-rw($_,1= '' }
        when Range { $str.substr-rw($_  ) = '' }
    }
    return $str;
}
 
say '0123456789'[- 1..38 ];  # OUTPUT: «045679␤» 

字符串:如何将字符串分成相等的部分?§

.comb 接受一个可选的 Int

.say for 'abcdefghijklmnopqrstuvwxyz'.comb: 8;  # OUTPUT: «abcdefgh␤ijklmnop␤qrstuvwx␤yz» 

语言特性§

如何转储 Raku 数据结构(类似 Perl Data::Dumper 和类似的)?§

典型选项是使用 say 例程,它使用 gist 方法,该方法提供被转储对象的“概要”。可以通过调用 perl 方法(很快将在 Rakudo 2019.11 版本发布后弃用,支持 $obj.raku)来获得更详细的输出,该方法通常以 EVAL 可执行代码的形式返回对象的表示。

如果您使用的是 rakudo 实现,您可以使用 rakudo 特定的 dd 例程 进行转储,其输出类似于 raku,但包含更多信息。

示例

my $foo = %foo => 'bar' );
say $foo.raku;   # OUTPUT: «${:foo("bar")}␤» 
say $foo;        # OUTPUT: «{foo => bar}␤» 
 
# non-standard routine available in rakudo implementation: 
dd $foo;         # OUTPUT: «Hash $foo = ${:foo("bar")}␤» 

还有一些 生态系统模块 提供了对数据结构转储方式的更多控制,包括对彩色输出的支持。

为什么 Rakudo 编译器如此道歉?§

如果输出中存在 SORRY!,则错误是编译时错误。否则,它是运行时错误。

示例

sub fooInt $aInt $b ) {...}
foo(1)     # ===SORRY!=== Error while compiling ... 
say 1/0;   # Attempt to divide 1 by zero using div 

什么是 (Any)§

Any 是大多数对象继承的顶级类。 Any 类型对象是 变量和参数的默认值,没有显式类型约束,这意味着当您输出没有值的变量的 概要 时,您可能会看到打印 (Any),例如,使用 say 例程

my $foo;
say $foo# OUTPUT: «(Any)␤» 
 
my Int $baz;
say $baz# OUTPUT: «(Int)␤» 
 
my $bar = 70;
say $bar# OUTPUT: «70␤» 

要测试变量是否具有任何定义的值,请参见 DEFINITEdefined 例程。还存在其他几个结构用于测试确定性,例如 withorwithwithout 语句、//andthennotandthenorelse 运算符,以及 类型约束表情符号

什么是 so§

so 是一个松散优先级运算符,它强制转换为 Bool

它的语义与 ? 前缀运算符相同,就像 and&& 的低优先级版本一样。

示例

say so 1|== 2;    # OUTPUT: «True␤» 

在此示例中,比较的结果(它是 Junction)在打印之前被转换为 Bool。

签名中的 :D:U 是什么?§

在 Raku 中,类和其他类型是对象,并通过其自身类型的类型检查。

例如,如果您声明一个变量

my Int $x = 42;

那么您不仅可以将整数(即类 Int 的实例)分配给它,还可以将 Int 类型对象本身分配给它

$x = Int

如果您想排除类型对象,您可以附加 :D 类型表情符号,它代表“确定”。

my Int:D $x = 42;
$x = Int;
 
# dies with: 
# Type check failed in assignment to $x; 
# expected Int:D but got Int 

同样,:U 限制为未定义的值,即类型对象。

要明确允许类型对象或实例,您可以使用 :_

签名中的 --> 是什么?§

--> 是一个返回约束,可以是类型或确定值。

类型约束示例

sub divide-to-intInt $aInt $b --> Int ) {
        return ($a / $b).narrow;
}
 
divide-to-int(32)
# Type check failed for return value; expected Int but got Rat 

确定返回值示例

sub discard-random-number--> 42 ) { rand }
say discard-random-number;
# OUTPUT: «42␤» 

在这种情况下,最终值被丢弃,因为返回值已在签名中指定。

如何从 Junction 中提取值?§

如果您想从 Junction 中提取值(本征态),那么您可能做错了什么,应该使用 Set 代替。

连接符旨在用作匹配器,而不是用于进行代数运算。

如果你想无论如何都要这样做,你可以滥用自动线程来实现。

sub eigenstates(Mu $j{
    my @states;
    -> Any $s { @states.push: $s }.($j);
    @states;
}
 
say eigenstates(1|2|3).join('');
# prints 1, 2, 3 or a permutation thereof 

如果 Str 是不可变的,那么 s/// 如何工作?如果 Int 是不可变的,那么 $i++ 如何工作?§

在 Raku 中,许多基本类型的值是不可变的,但保存它们的变量不是。s/// 运算符作用于一个变量,它将一个新创建的字符串对象放入该变量中。同样,$i++ 作用于 $i 变量,而不仅仅作用于其中的值。

了解这一点后,你不会尝试更改字面字符串(例如,像 'hello' ~~ s/h/H/;),但你可能会意外地使用 map 做出类似的事情。

my @foo = <hello world>.map: { s/h/H/ };
 
# dies with 
# Cannot modify an immutable Str (hello) 
 
my @bar = <hello world>».subst-mutate: 'h''H';
 
# dies with 
# Cannot resolve caller subst-mutate(Str: Str, Str); 
# the following candidates match the type but require 
# mutable arguments: ... 

不要修改原始值,而是使用返回新值的例程或运算符。

my @foo = <hello world>.map: { S/h/H/ };  # ['Hello','world'] 
my @bar = <hello world>».subst: 'h''H'# ['Hello','world'] 

有关更多信息,请参阅有关 容器 的文档。

数组引用和自动解引用是怎么回事?我需要 @ 符号吗?§

在 Raku 中,几乎所有东西都是引用,因此谈论获取引用没有多大意义。标量变量也可以直接包含数组。

my @a = 123;
say @a;                 # OUTPUT: «[1 2 3]␤» 
say @a.^name;           # OUTPUT: «Array␤» 
 
my $scalar = @a;
say $scalar;            # OUTPUT: «[1 2 3]␤» 
say $scalar.^name;      # OUTPUT: «Array␤» 

最大的区别在于,标量内的数组在列表上下文中充当一个值,而数组将被愉快地迭代。

my @a = 123;
my $s = @a;
 
for @a { ... }          # loop body executed 3 times 
for $s { ... }          # loop body executed only once 
 
my @flat = flat @a@a;
say @flat.elems;            # OUTPUT: «6␤» 
 
my @nested = flat $s$s;
say @nested.elems;          # OUTPUT: «2␤» 

你可以使用 @( ... ) 或在表达式上调用 .list 方法来强制列表上下文,使用 $( ... ) 或在表达式上调用 .item 方法来强制项目上下文。

请参阅 Perl 6: 符号、变量和容器 文章以了解更多信息。

为什么使用符号?你不能没有它们吗?§

有几个原因:

  • 它们使将变量插入字符串变得容易。

  • 它们为不同的变量和 twigil 形成微命名空间,从而避免命名冲突。

  • 它们允许轻松进行单数/复数区分。

  • 它们的工作方式类似于使用强制名词标记的自然语言,因此我们的大脑天生就能处理它。

  • 它们不是强制性的,因为你可以声明无符号名称(如果你不介意歧义)。

"类型 Str 不支持关联索引。"§

你可能尝试混合字符串插值和键字符,例如 HTML 标签。

my $foo = "abc";
say "$foo<html-tag>";

Raku 认为 $foo 是一个哈希,而 <html-tag> 是一个字符串文字哈希键。使用闭包来帮助它理解你。

my $foo = "abc";
say "{$foo}<html-tag>";

Raku 有协程吗?yield 呢?§

Raku 没有像 Python 那样的 yield 语句,但它确实通过惰性列表提供了类似的功能。有两种流行的方法可以编写返回惰性列表的例程。

# first method, gather/take 
my @values = gather while have_data() {
    # do some computations 
    take some_data();
    # do more computations 
}
 
# second method, use .map or similar method 
# on a lazy list 
my @squares = (1..*).map(-> \x { x² });

为什么我不能从 new 方法初始化私有属性,以及如何解决这个问题?§

以下代码示例中的 say 语句

class A {
    has $!x;
    method show-x {
        return $!x;
    }
}
say A.new(x => 5).show-x;

不会打印 5。私有属性是私有的,这意味着外部世界不可见。如果默认构造函数可以初始化它们,它们就会泄漏到公共 API 中。因此,在这个特定的代码示例中,属性 $!x 在对象构造期间不会由默认构造函数初始化。

如果你仍然想使用默认构造函数初始化私有属性,你可以添加一个 submethod BUILD 来实现此任务。

class B {
    has $!x;
    submethod BUILD(:$!x{ }
    method show-x {
        return $!x;
    }
}
say B.new(x => 5).show-x;

BUILD 由默认构造函数调用(间接调用,有关更多详细信息,请参阅 对象构造),并使用用户传递给构造函数的所有命名参数。:$!x 是一个名为 x 的命名参数,当使用名为 x 的命名参数调用时,其值将绑定到属性 $!x

但是,你不应该这样做。如果属性被声明为私有,那么它不应该暴露给类外部的环境(例如,在对象构造期间)。另一方面,如果属性是公共的,那么用 $.x 声明它没有坏处,因为外部视图默认情况下是只读的,你仍然可以使用 $!x 在内部访问它。

sayputprint 之间有什么区别,为什么会有区别?§

最明显的区别是 sayput 在输出末尾追加一个换行符,而 print 则不追加。

但还有一个区别:printput 通过对传递给它们的所有项目调用 Str 方法将其参数转换为字符串,而 say 使用 gist 方法。gist 方法(你也可以为自己的类创建它)旨在为人类解释创建一个 Str。因此,它可以随意省略被认为对理解对象本质不重要的对象信息。

或者换句话说,$obj.Str 给出字符串表示,$obj.gist 提供适合人类快速识别的对象的简短摘要,而 $obj.raku 给出可以重新创建对象的 Raku 式表示。

例如,当在类型对象(也称为“未定义值”)上调用 Str 方法时,类型将被字符串化为一个空字符串,并抛出一个 warning。另一方面,gist 方法返回类型名称括在括号中(表示该值中除了类型之外什么都没有)。

my Date $x;     # $x now contains the Date type object 
print $x;       # empty string plus warning 
say $x;         # OUTPUT: «(Date)␤» 

如果你想显示对象的调试版本,最好使用 rakudo 特定的 dd 例程。它本质上执行 $obj.raku 并将结果显示在 STDERR 而不是 STDOUT 上,因此它不会干扰程序的任何“正常”输出。

简而言之,say 针对非正式的人类解释进行了优化,dd 针对非正式的调试输出进行了优化,而 printput 更普遍地适用于生成输出。

因此,putprintsay 的混合体;与 print 一样,它在对象上调用 Str 方法。与 say 一样,它在输出末尾添加一个换行符。

tokenrule 之间有什么区别?§

regextokenrule 引入了正则表达式,但语义略有不同。

token 意味着 :ratchet:r 修饰符,它阻止规则回溯。

rule 意味着 :ratchet:sigspace(简写为 :s)修饰符,这意味着规则不会回溯,并且它将正则表达式文本中的空格视为 <.ws> 调用(即匹配空格,除了两个单词字符之间,空格是可选的)。正则表达式开头和每个分支开头的空格将被忽略。

regex 声明一个没有隐式修饰符的普通正则表达式。

diefail 之间有什么区别?§

die 抛出一个异常。

fail 返回一个 Failure 对象。(如果调用者在调用词法范围内声明了 use fatal;fail 将抛出一个异常而不是返回它。)

Failure 是一个“未抛出”或“延迟”的异常。它是一个包含异常的对象,如果尝试将 Failure 用作普通对象或在接收器上下文中忽略它,它将抛出异常。

Failuredefined 检查中返回 False,你可以使用 exception 方法提取异常。

PointerOpaquePointer 之间有什么区别?§

OpaquePointer 已被弃用,并已被 Pointer 替换。

标识符中可以包含冒号对。有什么理由吗?§

标识符可以包含冒号对,它们成为其名称的一部分。根据 Larry Wall 对该问题的回答我们已经有了冒号对机制,因此使用它来扩展任何需要能够引用唯一化但非标准字符(或其他具有唯一字符串化的信息到此类字符)的名称是显而易见的

大多数人如何输入 Unicode 字符?§

这取决于操作系统、窗口环境和/或编辑器。 此页面介绍如何在最流行的操作系统和编辑器中输入 Unicode 字符

Raku 实现§

有哪些可用的 Raku 实现?§

目前发展最好的实现是 Rakudo(使用多个虚拟机后端)。历史上的实现包括 Niecza(.NET)和 Pugs(Haskell)。其他实现列在 Raku 编译器 页面。

Rakudo 用什么语言编写?§

简短的答案是 Rakudo 几乎完全用 Raku 编写。更详细的答案是 Rakudo 用 Raku 和 NQP(“Not Quite Perl”)混合编写。NQP 是一个轻量级的类似 Raku 的虚拟机环境;它旨在成为一种高级方式,使用 Raku 语法为虚拟机(如 MoarVM 和 JVM)创建编译器和库。

NQP 用什么语言编写?§

NQP 是 (1) NQP 代码、(2) 底层虚拟机使用的任何语言、(3) 一些第三方 C 和 Java 库以及 (4) 由构建过程的早期运行创建的一些引导文件的混合体。

Raku 是 Lisp 吗?§

(not (not Nil))

我可以将我的脚本编译成独立的可执行文件吗?§

App::InstallerMaker::WiX 这样的工具允许您创建安装程序,该安装程序将打包编译器和您的脚本。但是,目前可用的编译器尚不支持创建独立的可执行文件。

如果您想帮忙,Rakudo 编译器在 MoarVM 后端有一个 https://github.com/MoarVM/MoarVM/issues/875 问题,作为讨论这个问题的地方。

Raku 分发§

下一个版本的 Rakudo Star 什么时候发布?§

Rakudo Star 版本通常每季度发布一次,发布公告 发布在 rakudo.org 上

元问题和倡导§

为什么 Raku 最初被称为 Perl 6?§

… 与其他一些不暗示其他语言中更高版本可能暗示的所有内容的名称相反。

简短的答案是,这是 Larry 在 规则 1 下的选择。

社区认为 Perl 和 Raku 是姊妹语言——它们有很多共同点,解决了许多相同的问题空间,但 Raku 并不打算取代 Perl。事实上,这两种语言可以相互操作。

Raku 什么时候会准备好?它现在已经准备好使用了吗?§

编程语言及其编译器的准备情况不是一个二元决策。随着语言和实现的发展,它们变得越来越可用。根据您的需求,Raku 及其编译器可能适合您,也可能不适合您。

也就是说,版本 6.c(2015 年圣诞节)是 Raku 作为一种语言的第一个正式版本,以及一个验证套件和一个通过它的编译器。

我为什么要学习 Raku?它有什么优点?§

Raku 统一了许多其他编程语言中通常找不到的伟大理念。虽然其他几种语言提供了一些这些功能,但没有一种语言提供所有这些功能。

  • Raku 提供过程式、面向对象和函数式编程方法。

  • 易于使用的统一语法,使用不变的符号表示数据结构。

  • 完全基于字形的 Unicode 支持,包括附件 #29。

  • 简洁、更易读的正则表达式;提升到下一个可用性水平,具有更多功能。命名正则表达式提高了易用性。

  • 连接允许轻松检查多种可能性;例如,$a == 1|3|42$a 等于 1 或 3 或 42 吗?)。

  • 动态变量提供了一种词法范围内的替代全局变量的方法。

  • 强调可组合性和词法作用域,以防止“远程操作”;例如,导入始终是词法作用域的。

  • 易于理解的一致作用域规则和闭包。

  • 强大的面向对象,包括类和角色(所有内容都可以视为对象)。继承。子类型化。代码重用。

  • 对对象和元对象的内省(层层深入)。

  • 元对象协议,允许进行元编程,而无需生成或解析代码。

  • 子程序和方法签名,便于轻松解包位置参数和命名参数。

  • 多重分派,用于具有不同签名的同名子程序/方法,基于元数、类型和可选的附加代码。

  • 编译时错误报告,用于未知子程序或不可能的分派。

  • 可选的逐步类型检查,不会产生额外的运行时成本。带有可选的类型注释。

  • 基于编译器/运行时状态内省的先进错误报告。这意味着更实用、更精确的错误消息。

  • Phaser(类似于BEGIN/END)允许在作用域进入/退出、循环第一次/最后一次/下一次以及更多特殊上下文中执行代码。

  • 高级并发模型,适用于隐式和显式多处理,远远超出了原始线程和锁。Raku 的并发提供了一套丰富的(可组合的)工具。

  • 多核计算机的使用越来越普遍,借助 Raku,可以利用并行性,包括隐式并行性(例如,使用>>方法)和显式并行性(start { code })。这一点很重要,因为摩尔定律正在失效。

  • 提供结构化语言支持,以支持异步执行代码的编程。

  • 供应允许在发生某些事件时执行代码(例如,计时器、信号或文件系统事件)。

  • react/whenever/supply关键字允许轻松构建交互式、事件驱动的应用程序。

  • 在可能的情况下进行惰性求值,在需要或必要时进行急切求值。这意味着,例如,惰性列表,甚至无限惰性列表,例如斐波那契数列或所有素数。

  • 用于更快速、更接近底层的处理的本机数据类型。

  • 使用NativeCall,与 C 和 C++ 中的外部库的接口相当容易。

  • 使用Inline::Perl5Inline::Python,与 Perl(CPAN)和 Python 模块的接口相当容易。

  • 可以同时安装和加载模块的多个版本。

  • 由于更新和升级策略更简单,系统管理也得到了简化。

  • 由于Rat(有理数),简单的数值计算不会造成精度损失。

  • 可扩展的语法,用于解析数据或代码(Raku 使用它来解析自身)。

  • Raku 是一种高度可变的语言(定义自己的函数、运算符、特性和数据类型,这些类型会修改解析器)。

  • 大量的数据类型,以及创建自己的类型的可能性。

  • 具有适当边界检查的多维形状或本机数组。

  • 在语法解析的任何时间或匹配发生时执行代码。

  • 添加自定义运算符或添加特性与编写子程序一样简单。

  • 在任何运算符(系统或自定义添加)上自动生成超运算符。

  • 在各种后端上运行。目前是 MoarVM 和 JVM,JavaScript 正在开发中,可能还会出现更多后端。

  • 在执行期间对热点代码路径进行运行时优化(JIT)。

  • 在小型(例如,树莓派)和大型多处理器硬件上运行。

  • 基于垃圾收集:没有及时销毁,因此不需要引用计数。使用 Phaser 进行及时操作。

  • 方法可以在运行时混合到任何实例化的对象中;例如,允许添加带外数据。

  • 易于使用命令行界面,可以通过具有多重分派和自动使用消息生成的MAIN子程序访问。

  • 更少的代码行允许创建更紧凑的程序。名称的霍夫曼编码提高了可读性。

  • 使用简单的迭代器接口定义的惰性列表,任何类都可以通过最小限度地提供一个方法来提供。

  • 能够在标识符中使用连字符和其他非字母数字 ASCII 字符以及某些 Unicode 字符。(在测试中使用连字符而不是下划线在用户中通常被称为“kebab case”。另请参见“camel case”和“snake case”:https://en.wikipedia.org/wiki/Letter_case#Special_case_styles)。

  • Raku 的座右铭与 Perl 一直以来的座右铭相同:“Perl 与众不同。简而言之,Perl 的设计目的是让简单的任务变得简单,而不会让困难的任务变得不可能。”和“做一件事不止一种方法”。现在,更增添了 -Ofun。

Raku 速度够快吗?§

这取决于你正在做什么。Rakudo 的开发理念是“先让它正常工作,再让它快速工作”。它在某些方面已经很快,但在其他方面还需要改进。由于 Raku 为 JIT 提供了其他动态语言所没有的许多线索,我们认为在性能改进方面还有很大的空间。

以下粗略的基准测试(带有所有关于此类测试的常见警告)表明,如果使用 Raku 的全部功能,Raku 在类似任务中可能比 Perl 更快;同时,如果只使用基本功能,Perl 可能更快。在将 Raku 与其他语言进行比较时,也可以观察到类似的情况。

在你的系统上尝试一下。你可能会感到惊喜!

示例

# Raku version 
class Foo { has $.i is rw };
 
for 1..1_000_000 -> $i {
    my $obj = Foo.new;
    $obj.i = $i;
}
# Perl version
package Foo;
use Moose;

has i => (is => 'rw');

__PACKAGE__->meta->make_immutable;

for my $i (1..1_000_000) {
    my $obj = Foo->new;
    $obj->i($i);
}

1;

# Another Perl version that offers bare-bones set of features
# compared to Moose/Raku's version but those are not needed in this
# specific, simple program anyway.
package Foo;
use Mojo::Base -base;

has 'i';

for my $i (1..1_000_000) {
    my $obj = Foo->new;
    $obj->i($i);
}

1;

你可能也想使用这个程序来比较性能。它可以在两种语言下运行,只要使用 `perl -Mbigint` 来调用 Perl 即可。

my ($prev$current= (10);
 
for (0..100_000{
    ($prev$current= ($current$prev + $current);
}
print $current;