为什么要定义一个名为 !打破IRB?

Why does defining a method called ! break IRB?

IRB 在定义名为 !.

的方法时似乎有奇怪的行为

要重现这一点,请在 IRB 中输入以下内容:

def !
  puts "foo"
end

创建方法后,IRB 无限打印 foo:

irb(main):001:0> def !
irb(main):002:1>   puts "foo"
irb(main):003:1> end
foo
foo
foo
...

据我所知,您不能直接从 Ruby 语法调用名为 ! 的方法;您必须改用 send 编辑:您可以调用 ! 作为前缀运算符;这只是否定:!x

为什么这个定义会导致 IRB 无限循环? IRB 是否依赖名为 ! 的方法来打印其提示或类似的东西?

我在 Windows 10 上使用 Ruby 2.4.3 和 IRB 0.9.6。

tl;dr:在 class 之外覆盖 ! 是一件 非常 奇怪的事情!有无数种方法可以 "break" ruby 通过做这样疯狂的事情 - 所以你可能会发现玩弄这些奇怪的想法很有趣,但显然不要在重要的代码中这样做!

在ruby中,全部 class继承自top-level基础class:BasicObject。这 class defines top-level object negation - 即每当你写

!foo

这实际上是在您的对象 foo:

上调用一个名为 ! 方法
foo.send(:!)

这使得 重新定义 特定 class 上的方法成为可能(尽管这是一件非常罕见的事情!)。例如,在实现 null object pattern 时,您可以这样做:

class NullObject
  def !
    true
  end
end

my_null = NullObject.new
!!my_null #=> false

(通常,只有 对象 return false 在上面的行中是 nilfalse! )

那么,回到你的例子。您实际上在这里所做的是在 class Object 上定义一个名为 ! 的方法(并且没有调用 super 来触发原始方法!)。换句话说,您基本上 re-defined 响应是一种在内部 到处 使用的基本方法。某处某处 (??) 被这种奇怪的行为弄糊涂了,结果失败了 non-gracefully.

irb(main):001:0> def !
irb(main):002:1> puts "foo"
irb(main):003:1> super # <-- !! This stops it from breaking completely !!
irb(main):004:1> end
=> :!
irb(main):005:0* method(:!)
foo
foo
=> #<Method: Object#!>
irb(main):006:0> method(:!).source_location
foo
foo
=> ["(irb)", 1]
irb(main):007:0> method(:!).super_method
foo
foo
=> #<Method: BasicObject#!>

这里有一些其他方法可以 re-define 导致奇怪 behaviour/errors 的方法,例如:

def nil?
  true
end
# Will now die in weird ways!

class String
  def ===(other)
    true
  end
end

"ruby" === "awesome"
#=> true