[ruby-list:50709] Re: [Q]last_matchをブロックに渡す方法

From: "Masa Sakano" <imagine@...>
Date: 2018-10-10 22:03:27 UTC
List: ruby-list #50709
坂野正明です。

On 10 Oct 2018, at 15:55, Kazuhiro NISHIYAMA wrote:
> On Tue, 09 Oct 2018 19:21:33 +0900,
> Masa Sakano wrote:
>> Rubyのメソッドから、ブロックに $1, $2 (を含めた 
>> Regexp.last_match) を
>> 渡す方法がないものか、と質問します。
>
> local_variable_set(:$~, $~) ができないようなので、
> 一旦適当なローカル変数 
> (名前が衝突する可能性がある) に
> 入れて eval 
> で代入する方法しか思いつきませんでしたが、
> できないことはないようです。

binding で、local_variable_set で、それを eval ですか。
恐れ入りました。ありがとうございます!

確かに力技ながら、思っていたことがこれで実現できます。Rubyの潜在力の一旦に触れた思いです。
ブロックの後にもローカルスコープの $& 
がそのまま残ることも String#sub と同じですね。

ご指摘の変数の名前の衝突は、ローカル変数「`_`」(アンダースコア)を使用すれば、現実的には回避できることになりませんか。ローカル変数「`_`」はダミーの代入以外には使用しないのが、Ruby 
の慣習だったと理解しています(当メソッド内で、一瞬、その慣習に反することになりますが…)。

西山さんのご提示の例を書き換えると、以下のようになります。
ブロック実行後にローカル変数「`_`」をあえて読み出すと、内容が変更されていますが、そもそも読み出すものでないならば事実上の問題はない、と。

     def f(&block)
       /a/ =~ "a"
       block.binding.tap do |b|
         b.local_variable_set(:_, $~)
         b.eval("$~=_")
       end
       yield
       [1, 2]
     end
     /x/ =~ 'x'
     p $&    #=> "x"
     y, _, _ = $&, f{p $&} #=> "a"
     p y     #=> "x"
     p $&    #=> "a"
     p _     #=> "a"

西山さん、お知恵を貸してくださり感謝します!
(本MLへの投稿は10年ぶりくらいになりますが、助かりました。)

坂野正明

>
> def f(&block)
>   /a/ =~ "a"
>   block.binding.tap do |b|
>     b.local_variable_set(:_md, $~)
>     b.eval("$~=_md")
>   end
>   yield
> end
> f{p $&} #=> "a"
>
> -- 
> |ZnZ(ゼット エヌ ゼット)
> |西山和広(Kazuhiro NISHIYAMA)

In This Thread