奇怪的 Ruby

最近我为 prettier 写了一个 Ruby 版本的插件 plugin for prettier,在过程中我发现 Ruby 语言中很多奇怪的地方(通过需要考虑 AST 的每个节点类型以及结构的每个变体),我发现一些有趣的事情,并在这里与你分享。

case

Ruby 中的 case 表达式与其他语言的 Switch 语句非常相似,它们提供基于一个值来处理不同逻辑分支的方法。但你也可以使用没有谓词的 case 表达式,

case
when foo == 1
  ...
when bar == 2
  ...
end

这在功能上等同与检查 if foo == 1 和 elsif bar == 2

flip-flops

Flip-flops(名称来自电路设计)是最难懂的操作符之一。这篇文章有一个很好的解释。

lines.each do |line|
  next unless (line =~ start_pattern)..(line =~ end_pattern)

  puts line
end

基本上,这将遍历一些列表 lines,打印满足开始和结束时间条件的每一个行。

for 循环

你知道 Ruby 还有 for 循环吗?他们看起来像这样:

for num in [1, 2, 3] do
  puts num
end

这也意味着 in 是一个保留字,所以你也不能将其用作标识符。

hooks

Ruby 有两个特殊的 Block 块,它们内置在语言中让你可以 hook 脚本的开始和退出。我认为它们对于脚本编写特别有用,而对于应用程序级逻辑则不那么有用。他们看着像是:

BEGIN {
  puts 'script has started'
}

END {
  puts 'script has ended'
}

有趣的是这些 Block 不支持 do...end 写法,我不知道为什么。

数字

在 Ruby 中二进制,八进制和十六进制数字有一种特殊的语法表示,以下所有语句的值都为 true:

0b10 == 2   # 二进制
0o10 == 8   # 八进制
0x10 == 16  # 十六进制

最重要的是,您甚至可以删除八进制数重点额 o 而使用前缀 0010 == 8 即为 true,如果不小心这个可能很容易出现意想不到的问题!

procs

Procs 和 lambdas 都有一个使用方括号调用的特殊语法,比如:

add = ->(left, right) { left + right }
add[3, 4] # => 7

传递给方括号的参数数量与传递给 proc 的数量一致。因此,对于没有元素的 proc 或 lambdas,您可以完全去掉参数,如:

greet = -> { puts 'Hello, world!' }
greet[]

字符串

字符串有各种有趣的属性!下面是一些让我感到惊讶的事情。

%x 字面量

您可能知道反引号表达式(即 ls),它将生成一个进程在 shell 中执行,然后将 stdio 输出返回给调用者。(要小心请不要自己 RCE)

你可能不知道 %x 字面量,它们实际上是同一个东西(即 %x[ls])。不要与其他 %-字面量 混淆,像 %w%i 创建数组!

%q 字面量

你可以使用 %q%q 来创建一个字符串字面量(比如 %q[abc])。这实际上和使用单引号和双引号是一样的,区别是后者不需要对双引号进行转移,非常方便。

插值

Ruby 中正常的字符串插值看起来像这样 "a #{b} c",其中 b 是会响应 to_s 的变量( 即除 BasicObject 之外的任何东西 )。但是你知道如果像插入一个实例(instance)、类或全局变量可以不用大括号,也就是下面的代码完全有效:

@instance = 'instance'
@@class = 'class'
$global = 'global'

"#@instance #@@class #$global" == 'instance class global' # => true

它甚至可以在正则表达式中使用!如果用上面的表达式中的 / 替换双引号,你将获得带有插值变量的正则表达式。

? 字面量

我之所以把最好的留到最后,因为这真的是挺奇怪,以下两个语句在 Ruby 中都有效:

?a == 'a'
?\M-\C-a == "\x81"

事实证明 ? 允许您创建长度为 1 的字符串,并引入一些特殊的附加功能。有关进一步说明,请直接从 Ruby 文档中获取:

\cx or \C-x    控制字符,其中 x 是一个可打印的 ASCII 字符
\M-x           元字符,其中 x 是一个可打印的 ASCII 字符
\M-\C-x        元控制字符,其中 x 是一个可打印的 ASCII 字符
\M-\cx         如上
\c\M-x         如上
\c? or \C-?    删除,ASCII 7Fh (DEL)

void

在Ruby中,您可以使用 () 来表示 nil,因为它被视为没有语句的空表达式。这导致能够做各种奇怪的事情,比如 !(),其值为 true

tl;dr

Ruby非常具有表现力,它为您提供了许多方法。这与许多语言形成了鲜明的对比(例如,在 Python 之蝉 (The Zen of Python) 中,它指出 “应该有一种 - 最好只有一种 - 显而易见的方式”)。

当然这种方法有权衡。虽然它可以为您提供快速生产代码的能力,但如果有无数相似的处理方法,它也会降低阅读效率。这是 prettier 和其 Ruby 插件的明确目标之一 - 我们正在尝试标准化,以便您可以回到编写应用程序代码之中。

相关推荐