如何将 Regexp.last_match 传递给 Ruby 中的块
How to pass Regexp.last_match to a block in Ruby
有没有办法将最后一场比赛(实际上Regexp.last_match
)传递给Ruby中的块(迭代器)?
这里有一个示例方法作为 Srring#sub
的一种包装器来演示问题。它接受标准参数和块:
def newsub(str, *rest, &bloc)
str.sub(*rest, &bloc)
end
它适用于标准的仅参数情况,并且可以占用一个块;然而,像 $1、$2 等位置特殊变量在块内是不可用的。以下是一些示例:
newsub("abcd", /ab(c)/, '') # => "cd"
newsub("abcd", /ab(c)/){|m| } # => "d" ( == nil)
newsub("abcd", /ab(c)/){.upcase} # => NoMethodError
该块无法以与 String#sub(/..(.)/){}
相同的方式工作的原因是我认为与范围有关;特殊变量 $1、$2 等是局部变量(Regexp.last_match
)。
有什么办法可以解决吗?我想让方法 newsub
像 String#sub
一样工作,从某种意义上说,$1、$2 等在提供的块中可用。
编辑:根据 some past answers,可能没有办法实现这个……
根据问题 (Ruby 2),这是一种方法。它不漂亮,在各个方面都不是 100% 完美,但可以做到。
def newsub(str, *rest, &bloc)
str =~ rest[0] # => ArgumentError if rest[0].nil?
bloc.binding.tap do |b|
b.local_variable_set(:_, $~)
b.eval("$~=_")
end if bloc
str.sub(*rest, &bloc)
end
这样,结果如下:
_ = (/(xyz)/ =~ 'xyz')
p # => "xyz"
p _ # => 0
p newsub("abcd", /ab(c)/, '') # => "cd"
p # => "xyz"
p _ # => 0
p newsub("abcd", /ab(c)/){|m| } # => "cd"
p # => "c"
p _ # => #<MatchData "abc" 1:"c">
v, _ = , newsub("efg", /ef(g)/){.upcase}
p [v, _] # => ["c", "G"]
p # => "g"
p Regexp.last_match # => #<MatchData "efg" 1:"g">
深入分析
在上面定义的方法newsub
中,当给定一个block时,调用者线程中的局部变量$1等在block执行后被(重新)设置,这与String#sub
。但是,当没有给出块时,局部变量 $1 etc not 重置,而在 String#sub
中,无论是否给出块,$1 etc 总是被重置.
此外,调用者的局部变量_
在此算法中被重置。在 Ruby 的约定中,局部变量 _
用作虚拟变量,不应读取或引用其值。因此,这不会造成任何实际问题。如果语句 local_variable_set(:$~, $~)
有效,则不需要临时局部变量。然而,它不是,在 Ruby 中(至少从版本 2.5.1 开始)。请参阅 Kazuhiro NISHIYAMA 在 [ruby-list:50708].
中的评论(日语)
一般背景(Ruby 的规范)解释
这里有一个简单的例子来强调 Ruby 与这个问题相关的规范:
s = "abcd"
/b(c)/ =~ s
p # => "c"
1.times do |i|
p s # => "abcd"
p # => "c"
end
$&
、</code>、<code>
等特殊变量(相关,$~
(Regexp.last_match
),$'
等)
在本地范围内工作。在Ruby中,局部作用域继承父作用域中的同名变量。
在上面的例子中,变量s
被继承,</code>也是如此。
<code>do
块由 1.times
yield 编辑,方法 1.times
无法控制块内的变量,除了块参数(上例中的i
;n.b.,虽然Integer#times
没有提供任何块参数,但尝试接收一个块将被默默地忽略)。
这意味着yield-s一个block无法控制block中的</code>,<code>
等的方法,这些都是局部变量(尽管它们可能看起来像全局变量)。
字符串大小写#sub
现在,让我们分析一下 String#sub
块的工作原理:
'abc'.sub(/.(.)./){ |m| }
这里,方法sub
首先进行正则表达式匹配,因此像</code>这样的局部变量被自动设置。然后,它们(像<code>
这样的变量)在块中被继承,因为这个块与方法"sub"在相同的范围内。它们未从sub
传递到块,不同于块参数m
(这是一个匹配的字符串,或等同于$&
).
因此,如果方法 sub
定义在与块 不同的作用域 中,则 sub
方法无法控制局部变量块内,包括 </code>。 <em>不同的作用域</em> 表示 <code>sub
方法是用 Ruby 代码编写和定义的情况,或者实际上,所有 Ruby 方法除外其中一些不是用 Ruby 编写的,而是用与编写 Ruby 解释器相同的语言编写的。
Ruby的official document (Ver.2.5.1)在String#sub
的部分解释:
In the block form, the current match string is passed in as a parameter, and variables such as , , $`, $&, and $' will be set appropriately.
正确。在实践中,能够并且确实设置正则匹配相关的特殊变量如$1、$2等的方法仅限于一些内置方法,包括Regexp#match
、Regexp#=~
、Regexp#===
、String#=~
、String#sub
、String#gsub
、String#scan
、Enumerable#all?
和 Enumerable#grep
.
提示 1:String#split
似乎总是重置 $~
nil。
提示 2:Regexp#match?
和 String#match?
不更新 $~
,因此速度更快。
这里有一个小代码片段来强调示波器的工作原理:
def sample(str, *rest, &bloc)
str.sub(*rest, &bloc)
# non-nil if matches
end
sample('abc', /(c)/){} # => "c"
p # => nil
这里,方法 sample() 中的 </code> <em> 由 <code>str.sub
在同一范围内设置。这意味着方法 sample()
将无法(简单地)在给定的块中引用 </code>。</p>
<p>我指出了Ruby官方文档(Ver.2.5.1)<a href="https://ruby-doc.org/core-2.5.1/doc/regexp_rdoc.html#class-Regexp-label-3D~+operator" rel="nofollow noreferrer">section of Regular expression</a>中的声明</p>
<blockquote>
<p>Using <code>=~
operator with a String and Regexp the $~
global variable is set after a successful match.
具有误导性,因为
$~
是一个 预定义局部作用域 变量( 不是 全局变量),并且
$~
已设置(可能为零),无论最后一次尝试的匹配是否成功。
$~
和
等变量不是全局变量这一事实可能有点令人困惑。但是,嘿,它们是有用的符号,不是吗?
有没有办法将最后一场比赛(实际上Regexp.last_match
)传递给Ruby中的块(迭代器)?
这里有一个示例方法作为 Srring#sub
的一种包装器来演示问题。它接受标准参数和块:
def newsub(str, *rest, &bloc)
str.sub(*rest, &bloc)
end
它适用于标准的仅参数情况,并且可以占用一个块;然而,像 $1、$2 等位置特殊变量在块内是不可用的。以下是一些示例:
newsub("abcd", /ab(c)/, '') # => "cd"
newsub("abcd", /ab(c)/){|m| } # => "d" ( == nil)
newsub("abcd", /ab(c)/){.upcase} # => NoMethodError
该块无法以与 String#sub(/..(.)/){}
相同的方式工作的原因是我认为与范围有关;特殊变量 $1、$2 等是局部变量(Regexp.last_match
)。
有什么办法可以解决吗?我想让方法 newsub
像 String#sub
一样工作,从某种意义上说,$1、$2 等在提供的块中可用。
编辑:根据 some past answers,可能没有办法实现这个……
根据问题 (Ruby 2),这是一种方法。它不漂亮,在各个方面都不是 100% 完美,但可以做到。
def newsub(str, *rest, &bloc)
str =~ rest[0] # => ArgumentError if rest[0].nil?
bloc.binding.tap do |b|
b.local_variable_set(:_, $~)
b.eval("$~=_")
end if bloc
str.sub(*rest, &bloc)
end
这样,结果如下:
_ = (/(xyz)/ =~ 'xyz')
p # => "xyz"
p _ # => 0
p newsub("abcd", /ab(c)/, '') # => "cd"
p # => "xyz"
p _ # => 0
p newsub("abcd", /ab(c)/){|m| } # => "cd"
p # => "c"
p _ # => #<MatchData "abc" 1:"c">
v, _ = , newsub("efg", /ef(g)/){.upcase}
p [v, _] # => ["c", "G"]
p # => "g"
p Regexp.last_match # => #<MatchData "efg" 1:"g">
深入分析
在上面定义的方法newsub
中,当给定一个block时,调用者线程中的局部变量$1等在block执行后被(重新)设置,这与String#sub
。但是,当没有给出块时,局部变量 $1 etc not 重置,而在 String#sub
中,无论是否给出块,$1 etc 总是被重置.
此外,调用者的局部变量_
在此算法中被重置。在 Ruby 的约定中,局部变量 _
用作虚拟变量,不应读取或引用其值。因此,这不会造成任何实际问题。如果语句 local_variable_set(:$~, $~)
有效,则不需要临时局部变量。然而,它不是,在 Ruby 中(至少从版本 2.5.1 开始)。请参阅 Kazuhiro NISHIYAMA 在 [ruby-list:50708].
一般背景(Ruby 的规范)解释
这里有一个简单的例子来强调 Ruby 与这个问题相关的规范:
s = "abcd"
/b(c)/ =~ s
p # => "c"
1.times do |i|
p s # => "abcd"
p # => "c"
end
$&
、</code>、<code>
等特殊变量(相关,$~
(Regexp.last_match
),$'
等)
在本地范围内工作。在Ruby中,局部作用域继承父作用域中的同名变量。
在上面的例子中,变量s
被继承,</code>也是如此。
<code>do
块由 1.times
yield 编辑,方法 1.times
无法控制块内的变量,除了块参数(上例中的i
;n.b.,虽然Integer#times
没有提供任何块参数,但尝试接收一个块将被默默地忽略)。
这意味着yield-s一个block无法控制block中的</code>,<code>
等的方法,这些都是局部变量(尽管它们可能看起来像全局变量)。
字符串大小写#sub
现在,让我们分析一下 String#sub
块的工作原理:
'abc'.sub(/.(.)./){ |m| }
这里,方法sub
首先进行正则表达式匹配,因此像</code>这样的局部变量被自动设置。然后,它们(像<code>
这样的变量)在块中被继承,因为这个块与方法"sub"在相同的范围内。它们未从sub
传递到块,不同于块参数m
(这是一个匹配的字符串,或等同于$&
).
因此,如果方法 sub
定义在与块 不同的作用域 中,则 sub
方法无法控制局部变量块内,包括 </code>。 <em>不同的作用域</em> 表示 <code>sub
方法是用 Ruby 代码编写和定义的情况,或者实际上,所有 Ruby 方法除外其中一些不是用 Ruby 编写的,而是用与编写 Ruby 解释器相同的语言编写的。
Ruby的official document (Ver.2.5.1)在String#sub
的部分解释:
In the block form, the current match string is passed in as a parameter, and variables such as , , $`, $&, and $' will be set appropriately.
正确。在实践中,能够并且确实设置正则匹配相关的特殊变量如$1、$2等的方法仅限于一些内置方法,包括Regexp#match
、Regexp#=~
、Regexp#===
、String#=~
、String#sub
、String#gsub
、String#scan
、Enumerable#all?
和 Enumerable#grep
.
提示 1:String#split
似乎总是重置 $~
nil。
提示 2:Regexp#match?
和 String#match?
不更新 $~
,因此速度更快。
这里有一个小代码片段来强调示波器的工作原理:
def sample(str, *rest, &bloc)
str.sub(*rest, &bloc)
# non-nil if matches
end
sample('abc', /(c)/){} # => "c"
p # => nil
这里,方法 sample() 中的 </code> <em> 由 <code>str.sub
在同一范围内设置。这意味着方法 sample()
将无法(简单地)在给定的块中引用 </code>。</p>
<p>我指出了Ruby官方文档(Ver.2.5.1)<a href="https://ruby-doc.org/core-2.5.1/doc/regexp_rdoc.html#class-Regexp-label-3D~+operator" rel="nofollow noreferrer">section of Regular expression</a>中的声明</p>
<blockquote>
<p>Using <code>=~
operator with a String and Regexp the $~
global variable is set after a successful match.
具有误导性,因为
$~
是一个 预定义局部作用域 变量( 不是 全局变量),并且$~
已设置(可能为零),无论最后一次尝试的匹配是否成功。
$~
和 等变量不是全局变量这一事实可能有点令人困惑。但是,嘿,它们是有用的符号,不是吗?