最近我为 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
而使用前缀 0
,010 == 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 插件的明确目标之一 - 我们正在尝试标准化,以便您可以回到编写应用程序代码之中。