为什么在Ruby中将Regexp对象视为“虚假”?


16

Ruby有“的普遍想法感实性 ”和“ falsiness ”。

Ruby 确实有两个特定于Boolean对象的类,TrueClass并且FalseClass具有分别由特殊变量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-expression对其进行评估(请参见11.5.4.8.2)。

falsenil之外的其他对象均分类为真实对象。true是该类的唯一实例TrueClass(请参见15.2.5),true表达式将对其进行评估(请参见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也是真实的,但根据我的测试,它们不是:

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 文字的结果的对象是虚假的,而Regexp作为其他表达式的结果的对象是真实的

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

这是错误还是期望的行为?


有趣的是,这Regex.new("a")是事实。
mrzasa

!!//是错误的但是!!/r/真实的。确实很奇怪。
最多

@max 使用(RVM)Ruby 2.4.1为我!!/r/生成false
3limin4t0r

对不起,我的坏@ 3limin4t0r。你是对的。我一定做了一些非常愚蠢的事情,例如遗漏了一个感叹号。
最多

2
一个假设,我认为//in if // then被解释为一个测试(的快捷方式if //=~nil then)(无论采用哪种模式都总是虚假的),而不是一个Regexp实例。
卡西米尔和希波吕特

Answers:


6

这不是错误。发生的事情是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)。
马特

还有解析器的第二部分,其中即会发出警告。我不知道那是怎么回事。
马特

我认为“第二部分”是真正适用于此问题的部分。NODE_LIT与类型T_REGEXP。您在答案中张贴的是动态Regexp文字,即Regexp使用插值的文字,例如/#{''}/
约尔格W¯¯米塔格

@JörgWMittag我认为你是对的。仔细查看编译器和生成的字节码,看起来好像是在动态正则表达式的情况下,解析树被重写为显式地添加$_为编译器正常处理的节点,而在静态情况下,所有这些都由编译器处理。编译器。这对我来说是可耻的,因为“嘿,您可以在这里看到解析树的重写位置”为您提供了一个很好的答案。
马特

4

这是(据我所知)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

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


红宝石脚本启动时,$_ == nil

因此,代码:

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

被解释为:

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

...哪个返回假。

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

//是真的 就像在Ruby中的所有其它对象,除了nilfalse


除非直接在命令行上运行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并明确地对此值执行regex检查会更正常。

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


我仅以条件为例。!// #=> true具有相同的行为,并且不是有条件的。我找不到任何布尔型上下文(有条件的或无条件的),其行为符合预期。
约尔格W¯¯米塔格

@JörgWMittag您是说!// ? true : false退货true吗?我认为这又是同一点-解释如下:!(// =~ nil) ? true : false
汤姆·洛德

如果您$_ = 'hello world'在运行上述代码之前进行了手动设置,那么您将获得不同的结果-因为// =~ 'hello world',但不匹配nil
汤姆·罗德,

不,我的意思是!// 没有条件求和true。您引用的规范是关于Regexp条件中的文字的,但是在此示例中,没有条件,因此该规范不适用。
约尔格W¯¯米塔格

2
啊..是的,非常令人惊讶。但是,该行为似乎是相关的:puts !//; $_ = ''; puts !//-我想是因为解析器像宏一样扩展了它;它不一定需要在条件内吗?
汤姆·罗德,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.