在Ruby中,Regexp#~一元运算符如何取别名?

In Ruby, how can the Regexp#~ unary operator be aliased?

利用 Ruby 在其基本功能中提供的自由,我发现为该语言中使用的大多数运算符取别名相当容易,但 the Regexp#~ unary prefix operator 比较棘手。

第一个天真的方法是在 Regexp class 本身中给它起别名

class Regexp
  alias hit ~@ # remember that @ stands for "prefix version"
  # Note that a simple `alias_method :hit, :~@` will give the same result 
end

正如在下面的一些回答中指出的那样,这种方法 以某种方式与点符号调用形式一起工作,例如 /needle/.hit。然而,尝试执行 hit /needle/ 会引发 undefined method hit' for main:Object (NoMethodError)`

所以另一种天真的方法是在 Object 中定义这个方法,比如

class Object
  def ~@(pattern)
    pattern =~ $_
  end
end

然而,这是行不通的,因为 $_ 全局变量实际上是本地绑定的,不会保留它在调用上下文中的值,即 $_ 总是nil 在前面的片段中。

所以问题是,是否可以使用表达式 hit /needle/ 来恢复与 ~ /needle/ 相同的结果?

对我来说效果很好:

class Regexp
  alias_method :hit, :~ # both of them work
  # alias hit ~         # both of them work
end

$_ = "input data"

/at/.hit #=> 7
~/at/    #=> 7
/at/.hit #=> 7
~/at/    #=> 7

因此,由于已完成的问题现在抑制了它,主要障碍是 $_ 的范围狭窄。这就是 trace_var 可以提供帮助的地方:

trace_var :$_, proc { |nub| 
  $last_explicitly_read_line = nub
  #puts "$_ is now '#{nub}'" 
}
  

def reach(pattern)
  $last_explicitly_read_line =~ pattern
end


def first
  $_ = "It’s needless to despair."
end

def second
  first
  p reach /needle/
  $_ = 'What a needlework!'
  p reach /needle/
end


p reach /needle/

second
p reach /needle/

$_ = nil
p reach /needle/

所以基本的想法是在每次更改时将 $_ 的值存储在另一个变量中,该变量将在其他后续调用上下文中访问。这里它是用另一个全局变量实现的(不是本地绑定的,当然不像 $_),但是用其他实现可以获得相同的结果,比如在 [=15= 上定义一个 class 变量].

人们也可以尝试使用类似 binding_of_caller or binding_ninja 的方法,但我自己的方法失败了,当然它还带有其他依赖项,这些依赖项有其自身的局限性。