本页面试图索引 Ruby 和 Raku 之间语法和语义上的高级差异。任何在 Ruby 中有效但在 Raku 中必须以不同方式编写的内容都应列在此处(而许多 Raku 特性和习惯用法则不会)。

因此,这不能被误认为是 Raku 的初学者教程或概述;它旨在作为具有强大 Ruby 背景的 Raku 学习者的技术参考。

基本语法§

语句结束分号§

Ruby 通过换行符(以及一些例外)检测大多数语句的结束,只要表达式完整即可。通常通过将运算符悬挂在行尾来分解长表达式,以确保解析将继续

foo +     # In Ruby a trailing operator means parsing should continue
  bar +
  baz

在 Raku 中,您必须使用 ; 显式地终止语句,这允许更好地反馈并在分解长行时提供更大的灵活性。两个不需要显式 ; 的例外是块中的最后一个语句,以及块本身的右花括号之后(如果该行上没有其他内容)

my $x;
...;
if 5 < $x < 10 {
  say "Yep!";
  $x = 17         # No ; required before closing } 
}                 # No ; required after closing } because of the newline 
say "Done!";      # The ; is not required here if nothing follows

空格§

Ruby 允许在使用空格方面有惊人的灵活性,即使在严格模式下并打开警告的情况下也是如此

# unidiomatic but valid Ruby
puts"Hello "+
(people [ i]
    . name
    ) . upcase+"!"if$greeted[i]<1

Raku 也支持程序员的自由和创造力,但将语法灵活性与它的一致、确定性、可扩展语法的设计目标相平衡,该语法支持单遍解析和有用的错误消息,干净地集成诸如自定义运算符之类的功能,并且不会导致程序员意外地错误地表达他们的意图。此外,"代码高尔夫" 的做法略微淡化了;Raku 的设计目的是在概念上比在按键上更简洁。

因此,在语法中有一些地方,空格在 Ruby 中是可选的,但在 Raku 中是强制性的或禁止的。许多这些限制不太可能影响到现实生活中的 Perl 代码(例如,在数组变量及其方括号之间不允许使用空格),但有一些限制不幸地会与一些 Ruby 黑客的习惯编码风格发生冲突

  • 在参数列表的左括号之前不允许使用空格。

    foo (341); # Not right in Ruby or Raku (in Raku this would 
                   # try to pass a single argument of type List to foo) 
    foo(341);  # Ruby and Raku 
    foo 341;   # Ruby and Raku - alternative parentheses-less style 
  • 在关键字之后必须使用空格

if(a < 0); ...; end         # OK in Ruby

my $a...;
if ($a < 0{ ... }         # Raku 
if $a < 0 { ... }           # Raku, more idiomatic 
while(x > 5); ...; end      # OK in Ruby

my $x...;
while ($x > 5{ ... }      # Raku 
while $x > 5 { ... }        # Raku, more idiomatic 
  • 在后缀/后缀运算符(包括数组/哈希下标)之前不允许使用空格。

    seen [ :fish ] = 1    # Ruby, not idiomatic but allowed
    
    
    %seen< fish > = 1;    # Raku, no space allowed after 'seen' 
  • 如果与现有的后缀/后缀运算符冲突,则在中缀运算符之前需要使用空格。

    n<1     # Ruby (in Raku this would conflict with postcircumfix < >)
    
    
    $n < 1# Raku 

. 方法调用,.public_send§

方法调用语法使用点,就像 Ruby 一样

person.name    # Ruby

my $person...;
$person.name   # Raku 

要调用一个直到运行时才知道其名称的方法

object.public_send(methodname, args);  # Ruby

my $objectmy Str $methodnamemy @args...;
$object."$methodname"(@args);   # Raku 

如果您省略引号,那么 Raku 期望 $methodname 包含一个 Method 对象,而不是方法的简单字符串名称。

变量、符号、作用域和常见类型§

在 Ruby 中,变量使用符号主要用于指示作用域。$ 用于全局作用域,@@ 用于类作用域,@ 用于实例作用域,没有符号用于局部变量(包括参数)。& 符号也用于指示方法引用。符号以 : 为前缀,但它们不是变量,因此不是真正的符号。

在 Raku 中,符号主要用于指示包含的值实现的角色,表明值的类型(或至少是接口)。符号是不变的,无论变量如何使用 - 你可以将它们视为变量名称的一部分。

变量的作用域由声明本身指示(myhasour 等)。

变量作用域§

对于局部变量,Ruby 在赋值时使用隐式变量声明,并且仅限于当前块。在 Ruby 中,ifwhile 内置构造的内容不是块或作用域。

Raku 使用显式作用域指示符,并且从不隐式创建变量。你看到的每个 { ... } 都是一个作用域,包括条件或循环的主体。常用的作用域声明

foo = 7        # Ruby, variable scope is defined by first assignment and
               # extends to the end of the current block

my  $foo = 7;   # Raku, lexical scoped to the current block 
our $foo = 7;   # Raku, package scoped 
has $!foo = 7;  # Raku, instance scoped (attribute) 

$ 标量§

$ 符号始终与“标量”变量一起使用(例如 $name)。这些是单值容器。

这是最通用的变量类型,对它的内容没有限制。请注意,你仍然可以访问/使用它的内容,例如 $x[1]$x{"foo"}$f("foo")

@ 数组§

@ 符号始终与“数组”变量一起使用(例如 @months@months[2]@months[2, 4] 用于数组切片)。使用 @ 符号的变量只能包含执行 Positional 角色的内容,表示位置索引和切片功能。

  • 索引

    puts months[2]; # Ruby
    
    
    say @months[2]; # Raku 
  • 值切片

    puts months[8..11].join(',') # Ruby
    
    
    say @months[8..11].join(','# Raku 

% 哈希§

% 符号始终与“哈希”变量一起使用(例如 %calories%calories<apple>%calories<pear plum>)。使用 % 符号的变量只能包含执行 Associative 角色的内容。

Ruby 使用方括号来访问数组和哈希的值。Raku 使用花括号来表示哈希。可以使用尖括号版本,它始终自动引用其内容(没有引号的字符串)

副词可用于控制切片的类型。

  • 索引

    puts calories["apple"]  # Ruby
    
    
    say %calories{"apple"}# Raku 
    puts calories["apple"]  # Ruby
    puts calories[:apple]   # Ruby, symbols for keys are common
    
    
    say %calories<apple>;   # Raku - angle brackets instead of single-quotes 
    say %calories«"$key"»;  # Raku - double angles interpolate as double-quotes 
  • 值切片

    puts calories.values_at('pear', 'plum').join(',') # Ruby
    puts calories.values_at(%w(pear plum)).join(',')  # Ruby, pretty?
    
    
    say %calories{'pear''plum'}.join(',');          # Raku 
    say %calories<pear plum>.join(',');               # Raku (prettier) 
    my $keys = 'pear plum';
    say %calories« $keys ».join(','); # Raku, interpolated split 
  • 键/值切片

    puts calories.slice('pear', 'plum')  # Ruby >= 2.5
    
    
    say %calories{'pear''plum'}:kv.Hash;   # Raku - use :kv adverb 
    say %calories<pear plum>:kv.Hash;        # Raku (prettier version) 

& 子例程§

& 符号的使用方式与 Ruby 的 & 非常相似,用于引用命名子例程/运算符的函数对象而不调用它,即使用名称作为“名词”而不是“动词”。使用 & 符号的变量只能包含执行 Callable 角色的内容。

add = -> n, m { n + m } # Ruby lambda for an addition function
add.(2, 3)              # => 5, Ruby invocation of a lambda
add.call(2, 3)          # => 5, Ruby invocation of a lambda

my &add = -> $n$m { $n + $m }# Raku addition function 
&add(23);                      # => 5, you can keep the sigil 
add(23);                       # => 5, and it works without it 
def foo
  ...
end
foo_method = method(:foo);     # Ruby

sub foo { ... };
my &foo_method = &foo# Raku 
some_func(&say) # Ruby pass a function reference

sub some_func { ... };
some_func(&say# Raku passes function references the same way 

在 Ruby 中,我们通常将一个块作为最后一个参数传递,这在 DSL 中尤其常用。这可以是通过 yield 调用的隐式参数,也可以是带有 & 前缀的显式块。在 Raku 中,Callable 参数始终被列出并通过变量名调用(而不是 yield),并且有多种调用函数的方法。

# Ruby, declare a method and call the implicit block argument
def f
  yield 2
end

# Ruby, invoke f, pass it a block with 1 argument
f do |n|
  puts "Hi #{n}"
end

# Raku, declare a method with an explicit block argument 
sub f(&g:($)) {
  g(2)
}
 
# Raku, invoke f, pass it a block with 1 argument 
# There are several other ways to do this 
f(-> $n { say "Hi {$n}" }); # Explicit argument 
f -> $n { say "Hi {$n}" };  # Explicit argument, no parenthesis 
 
# Additionally, if 'f' is a method on instance 'obj' you can use ':' 
# instead of parenthesis 
my $obj...;
$obj.f(-> $n { say "Hi {$n}" });  # Explicit argument 
$obj.f: -> $n { say "Hi {$n}" };  # Explicit argument, no parenthesis 

* 贪婪参数/参数扩展§

在 Ruby 中,你可以声明一个参数来使用 * 前缀将传递的参数的其余部分吸收到一个数组中。它在 Raku 中的工作方式类似

def foo(*args); puts "I got #{args.length} args!"; end # Ruby

sub foo(*@args{ say "I got #{@args.elems} args!" }   # Raku 

上面的 Raku 版本略有不同,因为它在将参数吸收到 @args 中时,会自动删除一层嵌套(如果有)。

sub foo(*@args{ say @args.raku }
foo([1, [23], 4], 5, [67]);   # [1, [2, 3], 4, 5, 6, 7] 

要保留参数的结构,可以使用 **

sub foo(**@args{ say @args.raku }
foo([1, [23], 4], 5, [67]);  # [[1, [2, 3], 4], 5, [6, 7]] 

你可能希望将一个数组扩展为一组参数。在 Raku 中,这是使用 Slip | 完成的

args = %w(a b c)         # Ruby
foo(*args)

sub foo($q$r$s{ ... };
my @args = <a b c>;       # Raku 
foo(|@args);

Raku 有许多更高级的传递参数和接收参数的方法,请参见 签名捕获

枝节§

Raku 另外使用“枝节”,它们是关于变量的进一步指示,位于符号和变量名的其余部分之间。示例

变量描述
$foo没有枝节的标量
$!foo私有实例变量
$.foo实例变量访问器
$*foo动态作用域变量
$^foo块的位置(占位符)参数
$:foo块的命名(占位符)参数
$=fooPOD(文档)变量
$?FILE当前源文件名。? 枝节表示编译时值
$~foo解析器看到的子语言,不常见

虽然这些示例都使用$符号,但大多数可以使用@(位置)或%(关联)。

: 符号§

Raku 通常在 Ruby 使用符号的地方使用字符串。一个主要例子是在哈希键中。

address[:joe][:street] # Typical Ruby nested hash with symbol keys

my %address...;
%address<joe><street>  # Typical Raku nested hash with string keys 

Raku 有冒号对语法,有时看起来像 Ruby 符号。

:age            # Ruby symbol

# All of these are equivalent for Raku 
:age            ;# Raku pair with implicit True value 
:age(True)      ;# Raku pair with explicit True value 
age => True     ;# Raku pair using arrow notation 
"age" => True   ;# Raku pair using arrow notation and explicit quotes 

你可能可以不用显式值就使用冒号对,并假装它是一个 Ruby 符号,但这不是 Raku 的惯用法。

运算符§

许多运算符在 Ruby 和 Raku 中都有类似的用法

  • , 列表分隔符

  • + 数值加法

  • - 数值减法

  • * 数值乘法

  • / 数值除法

  • % 数值模运算

  • ** 数值幂运算

  • ! && || 布尔值,高优先级

  • not and or 布尔值,低优先级

你可以使用$x++代替x += 1作为递增变量的快捷方式。这可以用作前递增++$x(递增,返回新值)或后递增$x++(递增,返回旧值)。

你可以使用$x--代替x -= 1作为递减变量的快捷方式。这可以用作前递减--$x(递减,返回新值)或后递减$x--(递减,返回旧值)。

== != < > <= >= 比较§

Raku 中的比较在数值和字符串之间分开,以避免常见的错误。

  • == != < > <= >= 比较

  • eq ne lt gt le ge 字符串比较

例如,使用==尝试将值转换为数字,而eq尝试将值转换为字符串。

<=> 三元比较§

在 Ruby 中,<=>运算符返回 -1、0 或 1。在 Raku 中,它们返回Order::LessOrder::SameOrder::More

<=>强制比较为数值上下文。

leg(“Less, Equal, or Greater?”)强制比较为字符串上下文。

cmp执行<=>leg,具体取决于其参数的现有类型。

~~ 智能匹配运算符§

这是一个非常常见的匹配运算符,类似于 Ruby 中的===。以下是一些示例

my $foo...;
say "match!" if $foo ~~ /bar/;       # Regex match 
say "match!" if $foo ~~ "bar";       # String match 
say "match!" if $foo ~~ :(IntStr); # Signature match (destructure)

Ruby 中的等效代码为

foo = "bar"
puts "match 1!" if /bar/ === foo       # Regex match
puts "match 2!" if foo === "bar"       # String match
puts "match 3!" if String === foo      # Class match

请注意,在这种情况下,===不是对称的;在第一种和最后一种情况下,变量必须位于右侧。Ruby 中也没有等效于签名类的类。

有关此功能的更多信息,请参阅S03/智能匹配

& | ^ 数值按位运算§

& | ^ 布尔运算§

在 Raku 中,这些单字符运算符已被删除,并被替换为双字符运算符,这些运算符会将其参数强制转换为所需的上下文。

# Infix ops (two arguments; one on each side of the op)
+&  +|  +^  And Or Xor: Numeric
~&  ~|  ~^  And Or Xor: String
?&  ?|  ?^  And Or Xor: Boolean

# Prefix ops (one argument, after the op)
+^  Not: Numeric
~^  Not: String
?^  Not: Boolean (same as the ! op)

&. 条件链运算符§

Ruby 使用&.运算符来链接方法,而不会在其中一个调用返回 nil 时引发错误。在 Raku 中,使用.?来实现相同目的。

<< >> 数值左移、右移运算符,铲子运算符§

+<+>替换。

puts 42 << 3  # Ruby

say  42 +< 3# Raku 

请注意,Ruby 通常使用<<运算符作为“铲子运算符”,它类似于.push。这种用法在 Raku 中并不常见。

=>: 键值分隔符§

在 Ruby 中,=> 用于哈希字面量声明和参数传递中的键值对上下文中。当左侧是符号时,: 用作简写。

在 Raku 中,=> 是 Pair 运算符,其原理完全不同,但在许多情况下工作方式相同。

如果您在哈希字面量中使用 =>,那么用法非常相似

hash = { "AAA" => 1, "BBB" => 2 }  # Ruby, though symbol keys are more common

my %hash = ( AAA => 1BBB => 2 ); # Raku, uses ()'s though {} usually work 

? : 三元运算符§

在 Raku 中,这是用两个问号而不是一个问号,以及两个感叹号而不是一个冒号来拼写的。这种与常见三元运算符的偏差消除了几种情况的歧义,并使 false-case 更突出。

result     = (  score > 60 )  ? 'Pass'  : 'Fail'; # Ruby

my $score...;
my $result = ( $score > 60 ) ?? 'Pass' !! 'Fail'# Raku 

+ 字符串连接§

被波浪号替换。助记符:想想用针线将两个字符串“缝合”在一起。

$food = 'grape' + 'fruit'  # Ruby

my $food = 'grape' ~ 'fruit'# Raku 

字符串插值§

在 Ruby 中,"#{foo}s" 将嵌入双引号字符串中的块分隔开。在 Raku 中,去掉 # 前缀:"{$foo}s"。与 Ruby 一样,您可以将任意代码放入嵌入块中,它将在字符串上下文中呈现。

简单变量可以插值到双引号字符串中,而无需使用块语法

# Ruby
name = "Bob"
puts "Hello! My name is #{name}!"

# Raku
my $name = "Bob";
say "Hello! My name is $name!"

Ruby 中嵌入块的结果使用 .to_s 获取字符串上下文。Raku 使用 .Str.gist 来实现相同的效果。

复合语句§

条件语句§

if elsif else unless§

这在 Ruby 和 Raku 之间非常相似,但 Raku 使用 { } 来清楚地划分块。

# Ruby
if x > 5
    puts "Bigger!"
elsif x == 5
    puts "The same!"
else
    puts "Smaller!"
end

# Raku 
my $x...;
if $x > 5 {
    say "Bigger!"
} elsif $x == 5 {
    say "The same!"
} else {
    say "Smaller!"
}

将条件表达式绑定到变量的方式略有不同

if x = dostuff(); ...; end   # Ruby

sub dostuff() {...};
if dostuff() -> $x {...}     # Raku, block-assignment uses arrow 

unless 条件语句在 Raku 中只允许一个块;它不允许 elsifelse 子句。

case-when§

Raku 的 given-when 结构就像一系列 if-elsif-else 语句,它的直接对应物是 Ruby 的 case-when 结构。就像 Ruby 对 case-when 表达式中的每个条件使用 ===(case 等于)运算符一样,Raku 同样使用 given-when 中的智能匹配 ~~ 运算符。

它具有以下一般结构

given EXPR {
    when EXPR { ... }
    when EXPR { ... }
    default { ... }
}

在最简单的形式中,该结构如下

my $value...;
given $value {
    when "a match" {
        # do-something(); 
    }
    when "another match" {
        # do-something-else(); 
    }
    default {
        # do-default-thing(); 
    }
}

这很简单,因为标量值在 when 语句中匹配。更一般地说,匹配实际上是对输入值的智能匹配,因此可以使用更复杂的实体(如正则表达式)而不是标量值来进行查找。

循环§

while until§

基本保持不变;条件周围的括号是可选的,但如果使用,则不能紧跟关键字,否则它将被视为函数调用。将条件表达式绑定到变量的方式也略有不同

while x = dostuff(); ...; end    # Ruby

sub dostuff {...}...;
while dostuff() -> $x {...}      # Raku 

for .each§

for 循环在 Ruby 中很少见,相反,我们通常对可枚举对象使用 .each。对 Raku 的最直接翻译是使用 .map 来代替 .each.map,但我们通常直接使用 for 循环。

# Ruby for loop
for n in 0..5
    puts "n: #{n}"
end

# Ruby, more common usage of .each
(0..5).each do |n|
    puts "n: #{n}"
end

# Raku 
for 0..5 -> $n {
    say "n: $n";
}
 
# Raku, misusing .map 
(0..5).map: -> $n {
    say "n: $n";
}

在 Ruby 中,.each 的迭代变量是列表元素的副本,修改它不会对原始列表产生任何影响。请注意,它是引用的副本,因此您仍然可以更改它引用的值。

在 Raku 中,该别名是只读的(为了安全起见),因此行为与 Ruby 完全相同,除非您将 -> 更改为 <->

cars.each { |car| ... }    # Ruby; read-only reference

my @cars...;
for @cars  -> $car   {...} # Raku; read-only 
for @cars <-> $car   {...} # Raku; read-write 

流程中断语句§

与 Ruby 相同

  • 下一个

  • 重做

Ruby 的 break 在 Raku 中是 last

正则表达式 (regex / regexp)§

Raku 中的正则表达式与 Ruby 中的正则表达式有很大不同,并且功能更强大。例如,默认情况下会忽略空格,并且所有字符都必须转义。正则表达式可以轻松地组合和声明,以构建高效的语法。

Raku 正则表达式有很多强大的功能,特别是使用相同的语法定义整个语法。请参阅 正则表达式语法

.match 方法和 =~ 运算符§

在 Ruby 中,可以使用 =~ 正则表达式匹配运算符或 .match 方法对变量进行正则表达式匹配。在 Raku 中,使用 ~~ 智能匹配运算符或 .match 方法。

next if line   =~ /static/   # Ruby
next if line  !~  /dynamic/; # Ruby
next if line.match(/static/) # Ruby

my $line...;
next if $line  ~~ /static/;    # Raku 
next if $line !~~ /dynamic/ ;  # Raku 
next if $line.match(/static/); # Raku 

或者,可以使用 .match.subst 方法。请注意,.subst 是非变异的。请参阅 S05/Substitution

.sub.sub!§

在 Raku 中,通常使用 s/// 运算符进行正则表达式替换。

fixed = line.sub(/foo/, 'bar')        # Ruby, non-mutating

my $line...;
my $fixed = $line.subst(/foo/'bar'# Raku, non-mutating 
line.sub!(/foo/, 'bar')   # Ruby, mutating

my $line...;
$line ~~ s/foo/bar/;      # Raku, mutating 

正则表达式选项§

将任何选项从正则表达式的末尾移动到开头。这可能需要您在普通匹配(如 /abc/)上添加可选的 m

next if $line =~    /static/i # Ruby

my $line...;
next if $line ~~ m:i/static/# Raku 

空格被忽略,大多数内容必须用引号括起来§

为了提高可读性和可重用性,Raku 正则表达式中的空格不重要。

/this is a test/ # Ruby, boring string
/this.*/         # Ruby, possibly interesting string

/ this " " is " " a " " test /# Raku, each space is quoted 
/ "this is a test" /;           # Raku, quoting the whole string 
/ this .* /;                    # Raku, possibly interesting string 

特殊匹配器通常位于 <> 语法下§

Raku 正则表达式支持许多特殊匹配语法的用例。这里不会列出所有用例,但通常情况下,断言不会被 () 包围,而是被 <> 包围。

对于字符类,这意味着

  • [abc] 变成 <[abc]>

  • [^abc] 变成 <-[abc]>

  • [a-zA-Z] 变成 <[a..zA..Z]>

  • [[:upper:]] 变成 <:upper>

  • [abc[:upper:]] 变成 <[abc]+:Upper>

对于环视断言

  • (?=[abc]) 变成 <?[abc]>

  • (?=ar?bitrary* pattern) 变成 <before ar?bitrary* pattern>

  • (?!=[abc]) 变成 <![abc]>

  • (?!=ar?bitrary* pattern) 变成 <!before ar?bitrary* pattern>

  • (?<=ar?bitrary* pattern) 变成 <after ar?bitrary* pattern>

  • (?<!ar?bitrary* pattern) 变成 <!after ar?bitrary* pattern>

(与 <> 语法无关,“环视” /foo\Kbar/ 变成 /foo <( bar )> /

  • (?(?{condition))yes-pattern|no-pattern) 变成 [ <?{condition}> yes-pattern | no-pattern ]

最长标记匹配 (LTM) 替代了交替§

在 Raku 正则表达式中,| 执行最长标记匹配 (LTM),它根据一组规则来决定哪个交替匹配模棱两可的匹配,而不是根据哪个在正则表达式中先写。

要避免新的逻辑,请将 Ruby 正则表达式中的任何 | 更改为 ||

与文件相关的操作§

将文本文件中的行读取到数组中§

Ruby 和 Raku 都可以轻松地将文件中的所有行读取到单个变量中,并且在这两种情况下,每行都删除了换行符。

lines = File.readlines("file")   # Ruby

my @lines = "file".IO.lines;     # Raku, create an IO object from a string 

遍历文本文件中的行§

不建议将整个文件读入内存。Raku 中的 .lines 方法返回一个延迟序列,但将其分配给数组会强制读取文件。最好迭代结果

# Ruby
File.foreach("file") do |line|
    puts line
end

# Raku 
for "file".IO.lines -> $line {
    say $line
}

面向对象§

基本类、方法、属性§

Ruby 和 Raku 中的类定义方式类似,都使用 class 关键字。Ruby 使用 def 定义方法,而 Raku 使用 method

# Ruby
class Foo
    def greet(name)
        puts "Hi #{name}!"
    end
end

# Raku 
class Foo {
    method greet($name{
        say "Hi $name!"
    }
}

在 Ruby 中,你可以在不事先声明的情况下使用属性,你可以通过 @ 符号识别它是属性。你还可以使用 attr_accessor 及其变体轻松创建访问器。在 Raku 中,你使用 has 声明和各种符号。你可以使用 ! 符号表示私有属性,或使用 . 创建访问器。

# Ruby
class Person
    attr_accessor :age    # Declare .age as an accessor method for @age
    def initialize
        @name = 'default' # Assign default value to private instance var
    end
end

# Raku 
class Person {
    has $.age;              # Declare $!age and accessor methods 
    has $!name = 'default'# Assign default value to private instance var 
}

创建类的实例使用 .new 方法。在 Ruby 中,你必须在 initialize 中手动分配实例变量。在 Raku 中,你获得一个默认构造函数,它接受访问器属性的键值对,并且可以在 TWEAK 方法中进行进一步设置。与 Ruby 一样,你可以覆盖 new 本身以实现更高级的功能,但这很少见。

# Ruby
class Person
    attr_accessor :name, :age
    def initialize(attrs)
        @name = attrs[:name] || 'Jill'
        @age  = attrs[:age] || 42
        @birth_year = Time.now.year - @age
    end
end
p = Person.new( name: 'Jack', age: 23 )

# Raku 
class Person {
    has $.name = 'Jill';
    has $.age  = 42;
    has $.birth_year;
    method TWEAK {
        $!birth_year = now.Date.year - $!age;
    }
}
my $p = Person.newname => 'Jack'age => 23 )

私有方法§

Raku 中的私有方法在声明时在名称前加 !,并且使用 ! 而不是 . 调用。

# Ruby
class Foo
    def visible
        puts "I can be seen!"
        hidden
    end

    private
    def hidden
        puts "I cannot easily be called!"
    end
end

# Raku 
class Foo {
    method visible {
        say "I can be seen!";
        self!hidden;
    }
 
    method !hidden {
        say "I cannot easily be called!";
    }
}

需要注意的是,在 Ruby 中,子对象可以访问父对象的私有方法(因此它们更像是其他语言中的“受保护”方法)。在 Raku 中,子对象不能调用父对象的私有方法。

元编程§

以下是一些元编程的示例。请注意,Raku 使用插入符号将元方法与普通方法区分开来。

# Ruby
person = Person.new
person.class
person.methods
person.instance_variables

# Raku 
class Person {};
...
my $person = Person.new;
$person.^name;             # Raku, returns Person (class) 
$person.^methods;          # Raku, using .^ syntax to access metamethods 
$person.^attributes;

与 Ruby 一样,在 Raku 中,一切都是对象,但并非所有操作都等同于 .public_send。许多运算符是全局函数,它们使用类型化多重分派(带有类型的函数签名)来决定使用哪个实现。

5.public_send(:+, 3)    # => 8, Ruby

&[+](53)       # => 8, Raku, reference to infix addition operator 
&[+].^candidates # Raku, lists all signatures for the + operator 

有关更多详细信息,请参阅 元对象协议

环境变量§

Raku 模块库路径§

在 Ruby 中,用于指定模块额外搜索路径的环境变量之一是 RUBYLIB

$ RUBYLIB="/some/module/lib" ruby program.rb

在 Raku 中,这类似,你只需要更改名称。正如你可能猜到的,你只需要使用 RAKULIB

$ RAKULIB="/some/module/lib" raku program.raku

与 Ruby 一样,如果你没有指定 RAKULIB,则需要通过 use lib 编译指示在程序中指定库路径

# Ruby and Raku 
use lib '/some/module/lib';

其他§

从模块中导入特定函数§

在 Ruby 中,没有内置方法可以从模块中选择性地导入/导出方法。

在 Raku 中,你使用 is export 角色指定要导出的函数,并且具有此角色的所有子程序都将被导出。因此,以下模块 Bar 导出子程序 foobar,但不导出 baz

unit module Bar# remainder of the file is in module Bar { ... } 
sub foo($ais export { say "foo $a" }
sub bar($bis export { say "bar $b" }
sub baz($z{ say "baz $z" }

要使用此模块,只需 use Bar,函数 foobar 即可使用

use Bar;
foo(1);    #=> "foo 1" 
bar(2);    #=> "bar 2" 

如果你尝试使用 baz,编译时会引发“未声明的例程”错误。

一些模块允许选择性地导入函数,这将类似于

use Bar <foo># Import only foo 
foo(1);        #=> "foo 1" 
bar(2);        # Error! 

OptionParser,解析命令行标志§

Raku 中的命令行参数开关解析由 MAIN 子程序的参数列表完成。

# Ruby
require 'optparse'
options = {}
OptionParser.new do |opts|
    opts.banner = 'Usage: example.rb --length=abc'
    opts.on("--length", "Set the file") do |length|
        raise "Length must be > 0" unless length.to_i > 0
        options[:length] = length
    end
    opts.on("--filename", "Set the file") do |filename|
        options[:file] = filename
    end
    opts.on("--verbose", "Increase verbosity") do |verbose|
        options[:verbose] = true
    end
end.parse!

puts options[:length]
puts options[:filename]
puts 'Verbosity ', (options[:verbose] ? 'on' : 'off')

ruby example.rb --filename=foo --length=42 --verbose
    42
    foo
    Verbosity on

ruby example.rb --length=abc
    Length must be > 0

# Raku 
sub MAIN ( Int :$length where * > 0:$filename = 'file.dat'Bool :$verbose ) {
    say $length;
    say $filename;
    say 'Verbosity ', ($verbose ?? 'on' !! 'off');
}
raku example.raku --file=foo --length=42 --verbose
    42
    foo
    Verbosity on
raku example.raku --length=abc
    Usage:
      example.raku [--length=<Int>] [--file=<Any>] [--verbose]

请注意,Raku 在命令行解析错误时会自动生成完整的用法消息。

RubyGems,外部库§

请参阅 https://raku.land/,这里提供了越来越多的 Raku 库以及管理它们的工具。

如果您使用的模块尚未转换为 Raku,并且本文件中未列出替代方案,则其在 Raku 下的使用可能尚未得到解决。

您可以尝试使用 Inline::Ruby 从您的 Raku 程序中调用现有的 Ruby 代码。这使用嵌入的 ruby 解释器实例来运行从您的 Raku 脚本调用的 Ruby 代码。请注意,这是一个实验性库。您可以类似地使用 Inline::Perl5、Inline::Python 等调用其他语言的库。