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

From: "Masa Sakano" <imagine@...>
Date: 2018-10-14 21:24:54 UTC
List: ruby-list #50710
坂野正明です。

先週投稿した本件につき、ご返事いただいたことを受けて、Stackoverflow 
で立てた質問スレッド
https://stackoverflow.com/questions/52359278/how-to-pass-regexp-last-match-to-a-block-in-ruby/52385870
でも、回答しておきました(英語)。西山さんのお名前と 
bladeの過去ログも参照させて頂いています。
関連して、同サイトの(ある人による) 
5年前の別の質問にも回答を加えておきました。
https://stackoverflow.com/questions/18550434/using-1-2-etc-global-variables-inside-method-definition/52783055#52783055

また、本件を活用したライブラリを、RubyGems 
にアップロード(更新)しました(Ver.1.0)。
https://rubygems.org/gems/file_overwrite
(なぜかアップロードから48時間以上経つのにDocumentation 
が自動生成された様子がありませんが……、503とかにも遭遇したので、サーバーの調子ですかね。Github 
ならばREADME 
が一応読めて、あるいはGemをダウンロードしてローカルで 
yard を走らせればドキュメント生成されます。
https://github.com/masasakano/file_overwrite
)

同ライブラリでは、sub に加えて gsub の 
duck-typingも実装しています。subよりはかなり面倒でしたが、なんとか実装できました。もし興味がある方がいらしたらご覧になるなり再利用なさってくだされば(MITライセンスです)。ただし、いずれも、ブロックが与えられない時には、(String#sub 
などとは違って)文実行後のcallerのスコープの $1 
などには正規表現の結果は反映されません。それも実現するには、caller 
のスコープの binding 
が必要だとすれば、簡単ではなさそうですね。TracePoint 
あたりを無理に使えば不可能ではないかも知れないと想像しますが……、仮にできてもそれはオーバーヘッドが極めて大きいでしょうし、現実的ではなさそうです。

以上、事後報告でした。
西山さん、あらためてありがとうございました!

坂野正明


On 10 Oct 2018, at 23:03, Masa Sakano wrote:

> 坂野正明です。
>
> 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

Prev Next