Discussion:
[ruby-list:50707] [Q]last_matchをブロックに渡す方法
Masa Sakano
2018-10-09 10:21:33 UTC
Permalink
坂野正明ず申したす。

Rubyのメ゜ッドから、ブロックに $1, $2 (を含めた
Regexp.last_match)
を枡す方法がないものか、ず質問したす。

背景ずしお、 内郚で String#sub (gsub)
を呌ぶような同名の(duck-typingの)メ゜ッドを䜜っおいたす。単玔化した䟋を挙げたす。

class C
# Stringのむンスタンス str に察しお、sub() を実行
def self.sub(str, re, &b)
str.sub(re, &b)
end
end

/(prev)/ =~ 'previous'
p C.sub('baa', /(b)aa/){|a| a+'_'+$1}
# => "baa_prev" (∵ ブロック䞭で ($1 == 'prev'))
# ここで "baa_b" を埗る方法があるか?

Rubyでは、䞀般に、$1
は、ロヌカル倉数ずしお扱われるず理解しおいたす。Ruby
のロヌカル倉数は、それを囲むスコヌプの倉数を匕き継ぎたすね。したがっお、䞊の䟋では、メ゜ッド
C.sub に䞎えるブロック䞭にある $1
には、そのブロックを含む文の盎前の正芏衚珟評䟡の結果("prev")が枡っおいたす。

しかし、ここで行いたいこずは、「クラス C
の(クラス)メ゜ッド C.sub
で評䟡された正芏衚珟の結果」を $1
(など)ずしおブロック内で扱うこずです。

珟実に、String#sub
などでは、メ゜ッド内で正芏衚珟評䟡した結果が $1
(など)ずしおブロックに枡されおいお、そのブロックの結果をメ゜ッド
sub() 偎から yieldできおいたす。それず同じこずを
Ruby蚀語で蚘述したメ゜ッド内で実珟するこずはできないものでしょうか?
それずも、String#sub などのように、(C蚀語などの)Ruby
interpreter実装蚀語で盎接蚘述するしかないのでしょうか?

この件は、Stackoverflow で質問スレッドを立おたした。
<https://stackoverflow.com/questions/52359278/how-to-pass-regexp-last-match-to-a-block-in-ruby/52385870>
回答がなかったので、䞀応、自分自身で「䞍可胜」ず回答したんですが、ひょっずしたら方法があるかも知れないず、こちらで質問する次第です。

䜕かヒントでもあれば、よろしくお願いしたす。

坂野正明
Kazuhiro NISHIYAMA
2018-10-10 14:55:56 UTC
Permalink
$B@>;3OB9-$G$9!#(B

On Tue, 09 Oct 2018 19:21:33 +0900,
Ruby$B$N%a%=%C%I$+$i!"%V%m%C%/$K(B $1, $2 ($B$r4^$a$?(B Regexp.last_match) $B$r(B
$BEO$9J}K!$,$J$$$b$N$+!"$H<ALd$7$^$9!#(B
local_variable_set(:$~, $~) $B$,$G$-$J$$$h$&$J$N$G!"(B
$B0lC6E,Ev$J%m!<%+%kJQ?t(B ($BL>A0$,>WFM$9$k2DG=@-$,$"$k(B) $B$K(B
$BF~$l$F(B eval $B$GBeF~$9$kJ}K!$7$+;W$$$D$-$^$;$s$G$7$?$,!"(B
$B$G$-$J$$$3$H$O$J$$$h$&$G$9!#(B

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($B%<%C%H(B $B%(%L(B $B%<%C%H(B)
|$B@>;3OB9-(B(Kazuhiro NISHIYAMA)
Masa Sakano
2018-10-10 22:03:27 UTC
Permalink
坂野正明です。
Post by Kazuhiro NISHIYAMA
On Tue, 09 Oct 2018 19:21:33 +0900,
Post by Masa Sakano
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幎ぶりくらいになりたすが、助かりたした。)

坂野正明
Post by Kazuhiro NISHIYAMA
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)
Masa Sakano
2018-10-14 21:24:54 UTC
Permalink
坂野正明です。

先週投皿した本件に぀き、ご返事いただいたこずを受けお、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
あたりを無理に䜿えば䞍可胜ではないかも知れないず想像したすが  、仮にできおもそれはオヌバヌヘッドが極めお倧きいでしょうし、珟実的ではなさそうです。

以䞊、事埌報告でした。
西山さん、あらためおありがずうございたした!

坂野正明
Post by Masa Sakano
坂野正明です。
Post by Kazuhiro NISHIYAMA
On Tue, 09 Oct 2018 19:21:33 +0900,
Post by Masa Sakano
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幎ぶりくらいになりたすが、助かりたした。)
坂野正明
Post by Kazuhiro NISHIYAMA
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)
Loading...