20

Ruby 对“真”和“”有一个普遍的看法。

Ruby确实有两个用于布尔对象的特定类,TrueClassFalseClass,单例实例分别由特殊变量true和表示false

然而,真假不限这两个类的实例,这个概念是通用的,适用于 Ruby 中的每一个对象。每个对象要么为,要么为。规则非常简单。特别是,只有两个对象是虚假的

每一个其他对象都是真实的。这甚至包括在其他编程语言中被认为是虚假的对象,例如

这些规则是内置在语言中的,不是用户可定义的。没有to_bool隐式转换或类似的东西。

这是来自ISO Ruby 语言规范的引用:

6.6 布尔值

对象被分类为真实对象虚假对象

只有falsenil是虚假对象。false是该类的唯一实例FalseClass(参见 15.2.6),false 表达式对其进行评估(参见 11.5.4.8.3)。nil是该类的唯一实例NilClass(参见 15.2.4),nil 表达式对其求值(参见 11.5.4.8.2)。

除了falsenil之外的对象被归类为 true 对象。true是类的唯一实例TrueClass(见 15.2.5),真表达式求值(见 11.5.4.8.3)。

可执行的 Ruby/Spec 似乎同意

it "considers a non-nil and non-boolean object in expression result as true" do
  if mock('x')
    123
  else
    456
  end.should == 123
end

根据这两个来源,我会假设Regexps 也是truthy,但根据我的测试,它们不是:

if // then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are falsy'

我在YARV 2.7.0-preview1TruffleRuby 19.2.0.1JRuby 9.2.8.0上对此进行了测试。所有三个实现都相互同意,但不同意 ISO Ruby 语言规范和我对 Ruby/Spec 的解释。

更准确地说,Regexp作为评估Regexp 文字结果的对象是falsy,而Regexp作为其他表达式结果的对象是truthy

r = //
if r then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are truthy'

这是一个错误,还是期望的行为?

4

2 回答 2

9

这不是错误。正在发生的事情是 Ruby 正在重写代码,以便

if /foo/
  whatever
end

有效地变成

if /foo/ =~ $_
  whatever
end

如果您在普通脚本中运行此代码(而不是使用该-e选项),那么您应该会看到一个警告:

warning: regex literal in condition

大多数时候这可能有点令人困惑,这就是给出警告的原因,但对于使用该-e选项的一行可能很有用。例如,您可以打印文件中与给定正则表达式匹配的所有行

$ ruby -ne 'print if /foo/' filename

(默认参数print也是$_。)

于 2019-10-11T13:30:27.940 回答
6

这是(据我所知)ruby 语言未记录的特性的结果,这个规范最好地解释了这一点:

it "matches against $_ (last input) in a conditional if no explicit matchee provided" do
  -> {
    eval <<-EOR
    $_ = nil
    (true if /foo/).should_not == true
    $_ = "foo"
    (true if /foo/).should == true
    EOR
  }.should complain(/regex literal in condition/)
end

您通常可以将其$_视为“最后读取的字符串gets

更令人困惑的是,$_(与 一起$-不是全局变量;它具有本地范围


当 ruby​​ 脚本启动时,$_ == nil.

所以,代码:

// ? 'Regexps are truthy' : 'Regexps are falsey'

被解释为:

(// =~ nil) ? 'Regexps are truthy' : 'Regexps are falsey'

...返回错误。

另一方面,对于非文字正则表达式(例如r = //or Regexp.new('')),这种特殊解释不适用。

//是真实的;就像红宝石中的所有其他对象一样,除了niland false


除非直接在命令行上运行 ruby​​ 脚本(即使用-e标志),否则 ruby​​ 解析器将显示针对此类用法的警告:

警告:条件中的正则表达式文字

可以在脚本中使用此行为,例如:

puts "Do you want to play again?"
gets
# (user enters e.g. 'Yes' or 'No')
/y/i ? play_again : back_to_menu

...但是将局部变量分配给结果gets并明确地对该值执行正则表达式检查会更正常。

我不知道使用正则表达式执行此检查的任何用例,尤其是在定义为文字值时。您强调的结果确实会让大多数 ruby​​ 开发人员措手不及。

于 2019-10-11T13:29:34.513 回答