Ruby 到 Raku

基本语法

语句结束分号

Ruby 使用换行(有几个例外)来探测大部分语句的结束, 只要表达式已经完成。通过把运算符挂在行的末尾以保证解析会继续而打断一个长的表达式的做法很常见:

  1. foo + # 在 Ruby 中结尾的运算符意味着解析会继续
  2. bar +
  3. baz

在 Raku 中你必须显式地使用 ; 来结束语句, 这允许更好的反馈和更灵活的断行。有两个例外不需要显式的 ;, 块儿中的最后一条语句, 在块自身的闭合花括号之后(如果那一行上没有任何其它东西):

  1. if 5 < $x < 10 {
  2. say "Yep!";
  3. $x = 17 # 在闭合花括号 } 之前不需要分号 ;
  4. } # 因为换行, 在闭合花括号 } 之后不需要分号 ;
  5. say "Done!"; # 如果后面什么也没有, 那么这儿的分号也不需要

空白

Ruby 中允许使用大量令人吃惊的灵活的空白, 即使在开启了严格模式和警告的情况下:

  1. # 不符合习惯但是在 Ruby 中是合法的
  2. puts"Hello "+
  3. (people [ i]
  4. . name
  5. ) . upcase+"!"if$greeted[i]<1

Perl 6 也遵从程序员的自由和创造力,但是平衡的语法灵活性与其设计目标是一致的—-确定性的,可扩展的语法,支持单程解析和有用的错误消息,组合功能,如利落地自定义运算符,不会导致程序员意外弄错他们的意图。 此外,不再强调 “代码高尔夫”; Raku 在概念上更为简洁, 而不是在少敲了几次键上。

因此,在语法中有很多地方,在 Ruby 中空格是可选的但是在 Raku 中却是强制的或禁止的。许多这些限制不太可能涉及很多现实的 Perl 代码(例如,在数组变量和它的方括号之间不允许有空格 ),但不幸的是有一些与某些 Ruby 黑客的习惯编码风格冲突:

  • 在参数列表的开括号「(」之前不允许有空格
  1. foo (3, 4, 1); # 在 Ruby 或 Raku 中都不正确 ( 在 Raku 中这会
  2. # 尝试为 foo 传递一个 List 类型的单个参数)
  3. foo(3, 4, 1); # Ruby 和 Raku 中都可以
  4. foo 3, 4, 1; # Ruby 和 Raku 中都可以 - 圆括号是可供选择的-less style
  • 关键字后面立刻需要跟着空格
  1. if(a < 0); ...; end # OK in Ruby
  2. if ($a < 0) { ... } # Raku
  3. if $a < 0 { ... } # Raku, 更地道
  4. while(x > 5); ...; end # OK in Ruby
  5. while ($x > 5) { ... } # Raku
  6. while $x > 5 { ... } # Raku, 更地道
  • 后缀/后环缀 操作符(包括数组/散列下标)前面不允许有空格。
  1. seen [ :fish ] = 1 # Ruby, 不地道, 但是允许这样写
  2. %seen< fish > = 1; # Raku, 'seen' 后面不允许出现空格
  • 中缀操作符之前需要空格, 如果它和已经存在的后缀/后环缀 操作符冲突的话。
  1. n<1 # Ruby (in Raku this would conflict with postcircumfix < >)
  2. $n < 1; # Raku

方法调用, .send

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

  1. person.name # Ruby
  2. $person.name # Raku

要调用一个直到运行时才直到名字的方法:

  1. object.send(methodname, args); # Ruby
  2. $object."$methodname"(@args); # Raku

如果你遗漏了双引号, 那么 Raku 会期望 $methodname 包含一个 Method 对象, 而不是单单是那个方法名的字符串表示。

变量、符号、作用域 和通用类型

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

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

变量的作用域改为由声明本身(my,has,our,etc)表示。

变量作用域

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

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

  1. foo = 7 # Ruby, variable scope is defined by first assignment and
  2. # extends to the end of the current block
  3. my $foo = 7 # Raku, lexical scoped to the current block
  4. our $foo = 7 # Raku, package scoped
  5. has $!foo = 7 # Raku, instance scoped (attribute)
$ 标量

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

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

@ 数组

@ 总是与”数组”变量一起使用(例如 @months@months[2]@months[2,4] 用于数组切片)。 使用 @ 符号的变量只能包含执行 Positional 角色的东西,Positional 角色指的是位置索引和切片功能。

  • 索引
  1. puts months[2]; # Ruby
  2. say @months[2]; # Raku
  • 值切片
  1. puts months[8..11].join(',') # Ruby
  2. say @months[8..11].join(',') # Raku
% 散列

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

Ruby 使用方括号来访问数组和哈希值。 Raku 使用花括号来代替散列。 尖括号版本也是可用的,它总是自动引起其内容(不带引号的字符串):

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

  • 索引
  1. puts calories["apple"] # Ruby
  2. say %calories{"apple"}; # Raku
  3. puts calories["apple"] # Ruby
  4. puts calories[:apple] # Ruby, symbols for keys are common
  5. say %calories<apple>; # Raku - angle brackets instead of single-quotes
  6. say %calories«$key»; # Raku - double angles interpolate as double-quotes
  • 值切片
  1. puts calories.values_at('pear', 'plum').join(',') # Ruby
  2. puts calories.values_at(%w(pear plum)).join(',') # Ruby, pretty?
  3. say %calories{'pear', 'plum'}.join(','); # Raku
  4. say %calories<pear plum>.join(','); # Raku (prettier)
  5. my $keys = 'pear plum';
  6. say %calories«$keys».join(','); # Raku, interpolated split
  • 键/值切片
  1. say calories.slice('pear', 'plum').join(','); # Ruby, with ActiveRecord
  2. say %calories{'pear', 'plum'}:kv.join(','); # Raku - 使用 :kv 副词
  3. say %calories<pear plum>:kv.join(','); # Raku (更好看的版本)
& Sub

& 符号与 Ruby 的 & 非常类似,用于引用一个具名的子例程/操作符的函数对象,而不调用它,即把名字用作”名词”而不是”动词”。 使用 & 符号的变量只能包含 Callable 角色的内容。

  1. add = -> n, m { n + m } # Ruby lambda for an addition function
  2. add.(2, 3) # => 5, Ruby invocation of a lambda
  3. add.call(2, 3) # => 5, Ruby invocation of a lambda
  4. my &add = -> $n, $m { $n + $m } # Raku addition function
  5. &add(2, 3) # => 5, you can keep the sigil
  6. add(2, 3) # => 5, and it works without it
  7. foo_method = &foo; # Ruby
  8. my &foo_method = &foo; # Raku
  9. some_func(&say) # Ruby pass a function reference
  10. some_func(&say) # Raku passes function references the same way

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

  1. # Ruby, declare a method and call the implicit block argument
  2. def f
  3. yield 2
  4. end
  5. # Ruby, invoke f, pass it a block with 1 argument
  6. f do |n|
  7. puts "Hi #{n}"
  8. end
  9. # Raku, declare a method with an explicit block argument
  10. sub f(&g:($)) {
  11. g(2)
  12. }
  13. # Raku, invoke f, pass it a block with 1 argument
  14. # There are several other ways to do this
  15. f(-> $n { say "Hi {$n}" }) # Explicit argument
  16. f -> $n { say "Hi {$n}" } # Explicit argument, no parenthesis
  17. f { say "Hi {$^n}" } # Implicit argument
  18. # Additionally, if 'f' is a method on instance 'obj' you can use C<:>
  19. # instead of parenthesis
  20. obj.f(-> $n { say "Hi {$n}" }) # Explicit argument
  21. obj.f: -> $n { say "Hi {$n}" } # Explicit argument, no parenthesis
  22. obj.f: { say "Hi {$^n}" } # Implicit argument, no parenthesis
* 吞噬参数/ 参数扩展

在 Ruby 中,你可以声明一个参数,使用 * 前缀将所传递参数的剩余部分传递到数组中。 它在 Raku 中的工作方式相同:

  1. def foo(*args); puts "I got #{args.length} args!"; end # Ruby
  2. sub foo(*@args) { say "I got #{@args.elems} args!" } # Raku

您可能想将数组扩展为一组参数。 在 Raku 中,这也使用 * 前缀:

  1. args = %w(a b c) # Ruby
  2. foo(*args)
  3. my @args = <a b c> # Raku
  4. foo(*@args)

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

Twigils

Raku 另外还使用 “twigs”,它是关于变量的进一步指示符,并且在符号和变量名的其余部分之间。 例子:

  1. $foo # Scalar with no twigil
  2. $!foo # 私有实例变量
  3. $.foo # Instance variable accessor
  4. $*foo # Dynamically scoped variable
  5. $^foo # A positional (placeholder) parameter to a block
  6. $:foo # 具名参数
  7. $=foo # POD (文档) 变量
  8. $?FILE # Current source filename. ? twigil 表明这是一个编译时值
  9. $~foo # Sublanguage seen by parser, uncommon

虽然每个例子都使用 $ 符号,但大多数可以使用 @(Positional)或 %(Associative)。

: 符号

Raku 通常在 Ruby 使用符号的地方使用字符串。 关于这点的一个主要例子是散列键。

  1. address[:joe][:street] # Typical Ruby nested hash with symbol keys
  2. %address<joe><street> # Typical Raku nested hash with string keys

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

  1. :age # Ruby symbol
  2. # All of these are equivalent for Raku
  3. :age # Raku pair with implicit True value
  4. :age(True) # Raku pair with explicit True value
  5. age => True # Raku pair using arrow notation
  6. "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

Less`,Order :: SameOrder :: More

<⇒ 用于强制数字上下文比较。

leg(”Less,Equal 或者 Greater?”)用于强制字符串上下文比较。

cmp 要么是 <⇒ 比较, 要么是 leg 比较,这取决于它的参数的现有类型。

~~ 智能匹配运算符

这是一个非常常见的匹配运算符,它不存在于 Ruby 中。这里有些例子:

  1. say "match!" if $foo ~~ /bar/; # Regex match
  2. say "match!" if $foo ~~ "bar"; # String match
  3. say "match!" if $foo ~~ :(Int, Str) # Signature match (destructure)

参见 S03/智能匹配

& | ^ 数字位操作

& | ^ 布尔运算

在 Raku 中,这些单字符操作被移除了,并被两个字符操作代替,它们将它们的参数强制到所需的上下文中。

  1. # Infix ops (two arguments; one on each side of the op)
  2. +& +| +^ And Or Xor: Numeric
  3. ~& ~| ~^ And Or Xor: String
  4. ?& ?| ?^ And Or Xor: Boolean
  5. # Prefix ops (one argument, after the op)
  6. +^ Not: Numeric
  7. ~^ Not: String
  8. ?^ Not: Boolean (same as the ! op)

&. 条件链式操作符

Ruby 使用 &. 运算符链接方法,而不会在一个返回 nil 的调用中产生错误。在 Raku 中因为同样的目的使用 .?

<< >> 数值左/右移位操作符,铲(shovel)操作符

替换为 +<+>

  1. puts 42 << 3 # Ruby
  2. say 42 +< 3; # Raku

注意,Ruby 经常使用 << 运算符作为”铲操作符”,这类似于`.push`。这种用法在 Raku 中不常见。

⇒`和 `: 键-值分隔符

在 Ruby 中, 用于 Hash 字面声明和参数传递的键/值对的上下文中。 当左边是符号时用 : 作速记符。

在 Raku 中, 是对(Pair)运算符,这在原理上是非常不同的,但在许多情况下工作相同。

如果你在哈希字面值中使用 ,那么用法非常类似:

  1. hash = { "AAA" => 1, "BBB" => 2 } # Ruby, though symbol keys are more common
  2. my %hash = ( AAA => 1, BBB => 2 ); # Raku, uses ()'s though {} usually work

?: 三目运算符

在 Raku 中,这被拼写为两个问号,而不是一个问号,和两个感叹号而不是一个冒号。这种与常见三目运算符的偏离消除了多种歧义的情况,并使得假的情况更突出。

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

+ 字符串连接

替换为波浪线符号(~)。助记符:想想用针和线缝合两个字符串。

  1. $food = 'grape' + 'fruit' # Ruby
  2. $food = 'grape' ~ 'fruit'; # Raku

字符串插值

在 Ruby 中,”{foo}s” 界定嵌入在双引号字符串中的块。在 Raku 中删除 前缀:”{$foo}s”。和 Ruby 一样,你可以将任意代码放在嵌入式块中,它将在字符串上下文中渲染。

简单变量可以插入到双引号字符串中,而不使用块语法:

  1. # Ruby
  2. name = "Bob"
  3. puts "Hello! My name is #{name}!"
  4. # Raku
  5. my $name = "Bob"
  6. say "Hello! My name is $name!"

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

复合语句

条件

if elsif else unless§

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

  1. # Ruby
  2. if x > 5
  3. puts "Bigger!"
  4. elsif x == 5
  5. puts "The same!"
  6. else
  7. puts "Smaller!"
  8. end
  9. # Raku
  10. if x > 5 {
  11. say "Bigger!"
  12. } elsif x == 5 {
  13. puts "The same!"
  14. } else {
  15. puts "Smaller!"
  16. }

将条件表达式绑定到变量上有一点不同:

  1. if x = dostuff(); ...; end # Ruby
  2. if dostuff() -> $x {...} # Raku, block-assignment uses arrow

unless 条件仅允许 Raku 中的单个块; 它不允许 elsifelse 子句。

cese-when

Raku 的 given-when 结构像一个 if-elsif-else 语句链或者类似于 Ruby 中的 case-when。一个很大的区别是,Ruby 使用 == 比较每个条件,但 Raku 使用更一般的智能匹配 ~~ 运算符。

它具有以下一般结构:

  1. given EXPR {
  2. when EXPR { ... }
  3. when EXPR { ... }
  4. default { ... }
  5. }

在其最简单的形式中,构造如下:

  1. given $value {
  2. when "a match" {
  3. do-something();
  4. }
  5. when "another match" {
  6. do-something-else();
  7. }
  8. default {
  9. do-default-thing();
  10. }
  11. }

这在 when 语句中匹配标量值的情况下是简单的。更一般地,匹配实际上是对输入值的智能匹配,使得可以使用更复杂的诸如正则表达式的实体的而非标量值来查找。

循环

while until

大部分不变;圆括号周围的条件是可选的,但如果使用了,不能立即跟随关键字,否则它将被视为一个函数调用。将条件表达式绑定到变量上也有一些不同:

  1. while x = dostuff(); ...; end # Ruby
  2. while dostuff() -> $x {...} # Raku
for .each

for 循环在 Ruby 中是罕见的,我们通常在可枚举上使用 .each。对 Raku 的最直接的翻译是对 .each.map 都使用 .map,但是我们通常直接使用 for 循环。

  1. # Ruby for loop
  2. for n in 0..5
  3. puts "n: #{n}"
  4. end
  5. # Ruby, more common usage of .each
  6. (0..5).each do |n|
  7. puts "n: #{n}"
  8. end
  9. # Raku
  10. for 0..5 -> $n {
  11. say "n: $n";
  12. }
  13. # Raku, mis-using .map
  14. (0..5).map: -> $n {
  15. say "n: $n";
  16. }

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

在 Raku 中,该别名是只读的(为了安全起见),因此它的行为与 Ruby 完全一样,除非把 改为 <→

  1. cars.each { |car| ... } # Ruby; read-only reference
  2. for @cars -> $car {...} # Raku; read-only
  3. for @cars <-> $car {...} # Raku; read-write

流程中断语句

与 Ruby 相同:

  • next

  • redo

  • break

这在 Raku 中是 last

正则表达式(Regex / Regexp)

Raku 中的正则表达式与 Ruby 中的正则表达式明显不同,它更强大。例如,默认情况下,Raku 将忽略空格,所有字符必须转移。正则表达式可以很容易地以组合和声明的方式建立高效的 grammars。

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

.match 方法和 =~ 运算符

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

  1. next if line =~ /static/ # Ruby
  2. next if $line ~~ /static/; # Raku
  3. next if line !~ /dynamic/ ; # Ruby
  4. next if $line !~~ /dynamic/ ; # Raku
  5. next if line.match(/static/) # Ruby
  6. next if $line.match(/static/); # Raku

或者,可以使用 .match`和 `.subst 方法。注意 .subst 是不可变的。参见 S05/替换

.sub.sub!

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

  1. fixed = line.sub(/foo/, 'bar') # Ruby, non-mutating
  2. my $fixed = $line.subst(/foo/, 'bar') # Raku, non-mutating
  3. line.sub!(/foo/, 'bar') # Ruby, mutating
  4. $line ~~ s/foo/bar/; # Raku, mutating

正则表达式选项

将任何选项从正则表达式的结尾移动到开头。这可能需要您在 /abc/ 等纯匹配中添加可选的 m

  1. next if $line =~ /static/i # Ruby
  2. next if $line ~~ m:i/static/; # Raku

空格被忽略,大多数东西必须被引起来

为了帮助可读性和可重用性,在 Raku 的正则表达式中,空格并不重要。

  1. /this is a test/ # Ruby, boring string
  2. /this.*/ # Ruby, possibly interesting string
  3. / this " " is " " a " " test / # Raku, each space is quoted
  4. / "this is a test" / # Raku, quoting the whole string
  5. / this .* / # Raku, possibly interesting string

特殊匹配器通常属于 <> 语法

Raku 的正则表达式有很多支持特殊匹配语法的情况。它们不会全部列在这里,但通常不是被 () 包围,断言将被 <> 包围。

对于字符类,这意味着:

  • [abc] 变为 <[abc]>

  • [^abc] 变为 ←[abc]>

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

  • 变为 <: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>

  • (Unrelated to <> syntax, the "lookaround" /foo\Kbar/ 变为 /foo <( bar )> /

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

最长令牌匹配(LTM)替代交替

在 Raku regexes 中,| 执行最长令牌匹配(LTM),它决定哪个备选分支根据一组规则赢得模棱两可的匹配,而不是根据在正则表达式中首先写出哪个备选分支。

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

文件相关操作

将文本文件的行读入数组

Ruby 和 Raku 都很容易将文件中的所有行读取到单个变量中,在这两种情况下,每一行都删除了换行符。

  1. lines = File.readlines("file") # Ruby
  2. my @lines = "file".IO.lines; # Raku, create an IO object from a string

迭代文本文件的行

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

  1. # Ruby
  2. File.foreach("file") do |line|
  3. puts line
  4. end
  5. # Raku
  6. for "file".IO.lines -> $line {
  7. say $line
  8. }

面向对象

基本类,方法,属性

在 Ruby 和 Raku 之间类的定义是相似的。 Ruby 使用 def 定义方法,而 Raku 使用 method 定义方法。

  1. # Ruby
  2. class Foo
  3. def greet(name)
  4. puts "Hi #{name}!"
  5. end
  6. end
  7. # Raku
  8. class Foo {
  9. method greet($name) {
  10. say "Hi $name!"
  11. }
  12. }

在 Ruby 中,你可以使用一个属性而不预先声明它,你可以告诉它这是一个属性,因为 @ 符号。您还可以使用 attr_accessor 及其变体轻松创建访问器。在 Raku 中,你使用 has 声明符和各种符号。你可以使用 ! twigil 作为私有属性或 . 创建一个访问器。

  1. # Ruby
  2. class Person
  3. attr_accessor :age # Declare .age as an accessor method for @age
  4. def initialize
  5. @name = 'default' # Assign default value to private instance var
  6. end
  7. end
  8. # Raku
  9. class Person {
  10. has $.age; # Declare $!age and accessor methods
  11. has $!name = 'default'; # Assign default value to private instance var
  12. }

使用 .new 方法创建类的新实例。在 Ruby 中,您必须在 initialize 内根据需要手动给实例变量赋值。在 Raku 中,您将获得一个接受访问器属性的键/值对的默认构造函数,并可以在 BUILD 方法中进一步设置。像 Ruby 一样,你可以重写 new 自身以获取更高级的功能,但这是罕见的。

  1. # Ruby
  2. class Person
  3. attr_accessor :name, :age
  4. def initialize(attrs)
  5. @name = attrs[:name] || 'Jill'
  6. @age = attrs[:age] || 42
  7. @birth_year = Time.now.year - @age
  8. end
  9. end
  10. p = Person.new( name: 'Jack', age: 23 )
  11. # Raku
  12. class Person
  13. has $.name = 'Jill';
  14. has $.age = 42;
  15. has $!birth_year;
  16. method BUILD {
  17. $!birth_year = now.Date.year - $.age;
  18. }
  19. }
  20. p = Person.new( name => 'Jack', age => 23 )

私有方法

Raku 中的私有方法声明的时候在他们的名字前置一个 ! 符号,并且调用的时候使用 ! 代替 .

  1. # Ruby
  2. class Foo
  3. def visible
  4. puts "I can be seen!"
  5. hidden
  6. end
  7. private
  8. def hidden
  9. puts "I cannot easily be called!"
  10. end
  11. end
  12. # Raku
  13. class Foo {
  14. method visible {
  15. say "I can be seen!"
  16. self!hidden
  17. }
  18. method !hidden {
  19. say "I cannot easily be called!"
  20. }
  21. }

一个重要的注意事项是,在 Ruby 中孩子对象可以看到父对象中的私有方法(所以他们更像是其他语言中的”受保护”的方法)。在 Raku 中,孩子对象不能调用父对象中的私有方法。

这里有一些元编程的例子。注意,Raku 将元方法与常规方法分离开了。

  1. person = Person.new # Ruby, create a new person
  2. my $person = Person.new # Raku, create a new person
  3. person.class # Ruby, returns Person (class)
  4. $person.WHAT # Raku, returns Person (class)
  5. person.methods # Ruby
  6. $person.^methods # Raku, using .^ syntax to access meta-methods
  7. person.instance_variables # Ruby
  8. $person.^attributes # Raku

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

  1. 5.send(:+, 3) # => 8, Ruby
  2. &link:5, 3[+] # => 8, Raku, reference to infix addition operator
  3. &[+].^candidates # Raku, lists all signatures for the + operator

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

环境变量

Perl 模块库路径

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

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

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

  1. $ PERL6LIB="/some/module/lib" raku program.p6

与 Ruby 一样,如果不指定 PERL6LIB,则需要通过 use lib 指令在程序中指定库路径:

  1. # Ruby and Raku
  2. use lib '/some/module/lib';

Misc.

从模块导入特定函数

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

在 Raku 中,通过在相关的 subs 上使用 “is export” 角色来指定要导出的函数,然后导出所有具有此角色的 subs。因此,下面的 Bar 模块导出 subs foobar,但不导出 baz

  1. unit module Bar; # remainder of the file is in module Bar { ... }
  2. sub foo($a) is export { say "foo $a" }
  3. sub bar($b) is export { say "bar $b" }
  4. sub baz($z) { say "baz $z" }

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

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

如果您尝试使用 baz, 那么在编译时会引发 “Undeclared routine” 的错误。

一些模块允许选择性地导入函数,它们看起来像:

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

OptionParser,解析命令行标志

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

  1. # Ruby
  2. require 'optparse'
  3. options = {}
  4. OptionParser.new do |opts|
  5. opts.banner = 'Usage: example.rb --length=abc'
  6. opts.on("--length", "Set the file") do |length|
  7. raise "Length must be > 0" unless length.to_i > 0
  8. options[:length] = length
  9. end
  10. opts.on("--filename", "Set the file") do |filename|
  11. options[:file] = filename
  12. end
  13. opts.on("--verbose", "Increase verbosity") do |verbose|
  14. options[:verbose] = true
  15. end
  16. end.parse!
  17. puts options[:length]
  18. puts options[:filename]
  19. puts 'Verbosity ', (options[:verbose] ? 'on' : 'off')
  20. ruby example.rb --filename=foo --length=42 --verbose
  21. 42
  22. foo
  23. Verbosity on
  24. ruby example.rb --length=abc
  25. Length must be > 0
  26. # Raku
  27. sub MAIN ( Int :$length where * > 0, :filename = 'file.dat', Bool :$verbose ) {
  28. say $length;
  29. say $data;
  30. say 'Verbosity ', ($verbose ?? 'on' !! 'off');
  31. }
  32. raku example.p6 --file=foo --length=42 --verbose
  33. 42
  34. foo
  35. Verbosity on
  36. raku example.p6 --length=abc
  37. Usage:
  38. c.p6 [--length=<Int>] [--file=<Any>] [--verbose]

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

RubyGems,外部库

请参阅 https://modules.raku.org/,其中提供了越来越多的 Raku 库以及管理它们的工具。

如果您使用的模块尚未转换为 Raku,并且本文档中未列出任何备选方案,那么它在 Raku 下的使用可能尚未解决。

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