Feature #3627

catchのブロックを再実行するメソッド

Added by Makoto Kishimoto over 4 years ago. Updated almost 4 years ago.

[ruby-dev:41892]
Status:Rejected
Priority:Low
Assignee:-

Description

=begin
catchとthrowで、ネストを飛び越えたbreakのようなことができるわけですが、同様のネストを飛び越えたnextのようなもの、を現在のrubyでやるのは少々面倒です。
retryを利用して、

tag = Class.new Exception
begin
 ...
 raise tag
 ...
rescue tag
 retry
end

という手もないではありませんが無理矢理っぽいです。
throwのかわりに、なにか別のメソッド(rewindという名前はどうでしょう?)を実行すると、catchのブロックが再実行される、というのはどうでしょうか?
=end

History

#1 Updated by Yukihiro Matsumoto over 4 years ago

=begin
まつもと ゆきひろです

In message "Re: [Feature #3627] catchのブロックを再実行するメソッド"
on Thu, 29 Jul 2010 17:33:11 +0900, Makoto Kishimoto redmine@ruby-lang.org writes:

|throwのかわりに、なにか別のメソッド(rewindという名前はどうでしょう?)を実行すると、catchのブロックが再実行される、というのはどうでしょうか?

基本的なアイディアを否定するわけではありませんが、

  • catchブロックの再実行が必要なユースケースが示されていない
  • rewindという名前はいろいろなところで使われているので望ま しくない

ので、現時点では賛成しません。

=end

#2 Updated by Makoto Kishimoto over 4 years ago

=begin
きしもとです

|throwのかわりに、なにか別のメソッド(rewindという名前はどうでしょう?)を実行すると、catchのブロックが再実行される、というのはどうでしょうか?

基本的なアイディアを否定するわけではありませんが、

  • catchブロックの再実行が必要なユースケースが示されていない
  • rewindという名前はいろいろなところで使われているので望ま しくない

ので、現時点では賛成しません。

ユースケースですが、The Art of Multiprocessor Programming の、
ロックフリースキップリストの find メソッド(Fig. 14.13)を Ruby で書いた
ものを示します。(begin と rescue と retry で同等のものを実装しています。
元の Java コードではラベル付きの continue でした)

 def find key, before_list, after_list
     retry_tag = Class.new Exception
     begin
         pp = nil
         p = @head
         @level_max.downto(0){|level|
             pp = p.links[level].get_link
             loop {
                 ppp, mark = pp.links[level].get
                 while mark do
                     snip = p.links[level].compare_and_set pp, ppp, false, false
                     unless snip then
                         raise retry_tag
                     end
                     pp = ppp
                     ppp, mark = pp.links[level].get
                 end
                 if pp.key.__send__(@cmp_op, key) == -1 then
                     p = pp
                     pp = ppp
                 else
                     break
                 end
             }
             before_list[level] = p
             after_list[level] = pp
         }
         if pp.key.__send__(@cmp_op, key) == 0 then
             pp
         else
             nil
         end
     rescue retry_tag
         retry
     end
 end
 private :find

また、しばらく考えてみたのですが、C言語で do { continue; } while (0); が
ループにならないことを考えると、デフォルトでは1回きりの実行が期待されている
catch との組み合わせはあまり良くないように思えてきました。

loop のブロックが引数を取るようにして、
loop {|tag|
 ...
 continue tag
 ...
}

というかたちではどうでしょうか?

あと、名前ですが、continue の他に loop と対になるので pool ...というのは
ダメですね多分。

=end

#3 Updated by Nobuyoshi Nakada over 4 years ago

=begin
なかだです。

At Sat, 31 Jul 2010 19:06:51 +0900,
KISHIMOTO, Makoto wrote in :

loop のブロックが引数を取るようにして、
loop {|tag|
 ...
 continue tag
 ...
}

というかたちではどうでしょうか?

こんなのではどうでしょうか。

class Loop
def loop
begin
t, val = catch(self) {
yield self
false
}
end while t
val
end
def next(*values)
Kernel.throw(self, [true, values])
end
def break(*values)
Kernel.throw(self, [false, values])
end
def self.loop(&block)
new.loop(&block)
end
end

i = 0
Loop.loop do |tag|
p(i += 1)
tag.next if i < 3
p "end"
tag.break
end

--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦

=end

#4 Updated by Makoto Kishimoto over 4 years ago

=begin
きしもとです

こんなのではどうでしょうか。

ありがとうございます。
while で catch を囲んだら、というアイディアは%rubyでいただいていたのですが、
うまく抽象化できるのですね。

=end

#5 Updated by Shyouhei Urabe over 4 years ago

  • Status changed from Open to Rejected

=begin
提案事態はrejectしますのでもし新しいアイディアができたらまた登録してください。
=end

Also available in: Atom PDF