【问题标题】:Why is a Regexp object considered to be "falsy" in Ruby?为什么在 Ruby 中 Regexp 对象被认为是“虚假的”?
【发布时间】:2023-04-05 22:17:01
【问题描述】:

Ruby 对“truthiness”和“falsiness”有一个普遍的想法。

Ruby确实有两个特定的布尔对象类,TrueClassFalseClass,单例实例分别由特殊变量 truefalse 表示。

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

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

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

这是ISO Ruby Language Specification的引述:

6.6 布尔值

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

只有 falsenil 是虚假对象。 false 是类 FalseClass 的唯一实例(参见 15.2.6),false-expression 对其求值(参见 11.5.4.8.3)。 nil 是类 NilClass 的唯一实例(参见 15.2.4),nil 表达式对其求值(参见 11.5.4.8.2)。 p>

falsenil 以外的对象被归类为真实对象。 true 是类 TrueClass 的唯一实例(参见 15.2.5),true-expression 对其求值(参见 11.5.4.8.3)。

The executable Ruby/Spec seems to agree:

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 也是真实,但根据我的测试,它们不是:

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 literals 的结果是 falsy,而 Regexp 对象是一些其他表达式的结果是真相

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

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

【问题讨论】:

  • 有趣的是Regex.new("a")是真的。
  • !!// 为假,但!!/r/ 为真。确实很奇怪。
  • @max !!/r/ 使用 (RVM) Ruby 2.4.1 为我生成 false
  • 对不起@3limin4t0r。你说的对。我一定是做了一些非常愚蠢的事情,比如漏掉了一个感叹号。
  • 一个假设,我认为if // then 中的// 被解释为测试(if //=~nil then 的快捷方式)(无论模式如何,它总是错误的)而不是正则表达式实例。

标签: regex ruby boolean jruby yarv


【解决方案1】:

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

if /foo/
  whatever
end

有效地变成

if /foo/ =~ $_
  whatever
end

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

warning: regex literal in condition

这在大多数情况下可能有点令人困惑,这就是给出警告的原因,但对于使用-e 选项的一行可能很有用。例如,您可以使用

打印文件中与给定正则表达式匹配的所有行
$ ruby -ne 'print if /foo/' filename

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

【讨论】:

  • 另请参阅-n-p-a-l 选项,以及仅在使用-n-p 时可用的少数内核方法( chompchopgsubsub)。
  • 还有a second part of the parser 发出警告。不过我不知道那里发生了什么。
  • 我相信“第二部分”是真正适用于这个问题的部分。 NODE_LIT 类型为 T_REGEXP。您在答案中发布的是动态Regexp 文字,即使用插值的Regexp 文字,例如/#{''}/.
  • @JörgWMittag 我认为你是对的。在编译器和生成的字节码中四处寻找,看起来在动态正则表达式的情况下,解析树被重写以显式添加 $_ 作为编译器正常处理的节点,而在静态情况下它全部处理由编译器使用。这对我来说很遗憾,因为“嘿,你可以看到这里的解析树被重写了”是一个很好的答案。
【解决方案2】:

这是(据我所知)ruby 语言未记录的特性的结果,this spec 对此进行了最佳解释:

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 读取的最后一个字符串”

为了让事情更加混乱,$_ (along with $-) is not a global variable; it has local scope


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

所以,代码:

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

被解释为:

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

...返回错误。

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

// 是真实的;就像除了 nilfalse 之外的 ruby​​ 中的所有其他对象。


除非直接在命令行上运行 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 的结果并显式地对该值执行正则表达式检查会更正常。

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

【讨论】:

  • 我只以条件为例。 !// #=&gt; true 具有相同的行为并且不在条件中。我找不到任何布尔上下文(条件或非条件),它的行为符合预期。
  • @JörgWMittag 你的意思是例如!// ? true : false 返回true?我认为这又是同一点——它被解释为:!(// =~ nil) ? true : false
  • 如果你在运行上述代码之前手动设置了$_ = 'hello world',那么你应该会得到不同的结果——因为// =~ 'hello world',但与nil不匹配。
  • 不,我的意思是!// 没有条件 计算结果为true。您引用的规范是关于条件中的 Regexp 文字,但在此示例中,没有条件,因此此规范不适用。
  • 啊.. 是的,非常令人惊讶。不过,这种行为似乎是相关的:puts !//; $_ = ''; puts !//——我想是因为解析器像宏一样扩展它;它不一定需要在条件内?
猜你喜欢
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多