Rubyで例外が発生したときに再試行する方法
すべてのエラーが致命的というわけではありません。再試行する必要があることを示すものもあります。幸い、Rubyには、「再試行」を容易にするいくつかの興味深いメカニズムが用意されていますが、すべてが明白またはよく知られているわけではありません。この投稿では、これらのメカニズムと、それらが実際にどのように機能するかを見ていきます。
retry
の紹介 わかりました-これは明らかですが、それが存在することを知っている場合に限ります。個人的には、楽しい「再試行」キーワードについて学ぶ前は、Rubyのキャリアに精通していました。
再試行は、Rubyの例外レスキューシステムに組み込まれています。とても簡単です。レスキューブロックで「再試行」を使用すると、レスキューされたコードのセクションが再度実行されます。例を見てみましょう。
begin
retries ||= 0
puts "try ##{ retries }"
raise "the roof"
rescue
retry if (retries += 1) < 3
end
# ... outputs the following:
# try #0
# try #1
# try #2
ここで注意すべき点がいくつかあります:
-
再試行が呼び出されると、すべて beginとrescueの間のコードが再度実行されます。それは間違いなくします ない 「中断したところから再開する」など。
-
再試行を制限するメカニズムを提供しないと、無限ループになってしまいます。
-
beginブロックとrescueブロックの両方のコードは、親スコープの同じ再試行変数にアクセスできます。
retry
の問題
再試行は素晴らしいですが、いくつかの制限があります。主なものは、beginブロック全体が再実行されることです。しかし、それが理想的でない場合もあります。
たとえば、単一のメソッド呼び出しを使用して、Twitter、Facebook、および他の多くのサイトにステータスの更新を投稿できるgemを使用していると想像してください。このように見えるかもしれません。
SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")
# ...posts to Twitter API
# ...posts to Facebook API
# ...etc
APIの1つが応答しない場合、gemはSocialMedia::TimeoutErrorを発生させて中止します。この例外をキャッチして再試行すると、再試行が最初からやり直されるため、投稿が重複してしまいます。
begin
SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")
rescue SocialMedia::TimeoutError
retry
end
# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# and so on
宝石に「Facebookをスキップして、投稿するAPIのリストを続けてください」と伝えることができたら素晴らしいと思いませんか。
幸いなことに、Rubyではまさにそれが可能です。
注:もちろん、この問題の本当の解決策は、ソーシャルメディアライブラリを再構築することです。しかし、これは、これから紹介するテクニックの唯一のユースケースではありません。
継続は人々を怖がらせる傾向があります。しかし、それはそれらがあまり頻繁に使用されておらず、少し奇妙に見えるからです。しかし、基本を理解すれば、それらは非常に単純です。
継続は、ビデオゲームの場合と同様に、コードの「保存ポイント」のようなものです。立ち去って他のことをした後、セーブポイントに戻ると、すべてがそのままになります。
...わかりました。完全な例えではありませんが、ある程度は機能します。いくつかのコードを見てみましょう:
require "continuation"
counter = 0
continuation = callcc { |c| c } # define our savepoint
puts(counter += 1)
continuation.call(continuation) if counter < 5 # jump back to our savepoint
あなたはいくつかの奇妙なことに気づいたかもしれません。それらを見ていきましょう:
-
callccメソッドを使用してContinuationオブジェクトを作成します。このためのクリーンなOO構文はありません。
-
初めての
continuation
変数が割り当てられ、callcc
の戻り値に設定されます のブロック。そのため、ブロックが存在する必要があります。 -
セーブポイントに戻るたびに、
continuation
変数には、call
を渡す引数が割り当てられます 方法。そのため、奇妙な外観のcontinuation.call(continuation)
を使用します 構文。
継続を使用してskip
を追加します すべての例外へのメソッド。以下の例は、それがどのように機能するかを示しています。例外を救済するときはいつでも、skip
を呼び出すことができるはずです。 、これにより、例外を発生させたコードが発生しなかったように動作します。
begin
raise "the roof"
puts "The exception was ignored"
rescue => e
e.skip
end
# ...outputs "The exception was ignored"
これを行うには、いくつかの罪を犯さなければなりません。 Exception
ただのクラスです。つまり、monekypatchを実行して、skip
を追加できます。 メソッド。
class Exception
attr_accessor :continuation
def skip
continuation.call
end
end
次に、continuation
を設定する必要があります すべての例外の属性。 raise
であることが判明しました は単なるメソッドであり、オーバーライドできます。
ところで、以下のコードは、Adviの優れたスライドデッキ「例外について知らなかったもの」からほぼ逐語的に引用されています。これよりも優れた実装方法は考えられませんでした:
require 'continuation'
module StoreContinuationOnRaise
def raise(*args)
callcc do |continuation|
begin
super
rescue Exception => e
e.continuation = continuation
super(e)
end
end
end
end
class Object
include StoreContinuationOnRaise
end
これで、skip
を呼び出すことができます 例外のメソッドであり、例外が発生しなかったようになります。
-
マトリックスとは何ですか?Rubyでそれを使用する方法は?
マトリックスは、スプレッドシートのようなデータを保存および操作するために使用できる2D(2次元)配列です。 次の用途に使用できます : テーブルゲーム(チェス、チェッカーなど)でボードを表す 統計とデータ分析 プロットとグラフの生成 これは強力なデータ構造であるため、使用方法を学ぶのに役立ちます。 Rubyでマトリックスを作成する方法 配列を使用して行列を作成できます。 このように : matrix = [ [1,2,3], [4,5,6], [7,8,9] ] これにより、3×3のマトリックスが生成されます。これは、2次元データをボードまたは位置のセットとして保
-
RubyでStructとOpenStructを使用する方法
Rubyの構造体とは何ですか? 構造体は組み込みのRubyクラスであり、値オブジェクトを生成する新しいクラスを作成するために使用されます。値オブジェクトは、関連する属性を一緒に格納するために使用されます。 ここに例があります : Point 2つの座標(x &y 。 このデータはさまざまな方法で表すことができます。 いいね : 配列[10, 20] ハッシュ{ x: 10, y: 10 } オブジェクトPoint.new(10, 20) 複数のPointを使用する場合 、オブジェクトアプローチを使用することをお勧めします。 しかし… これら2つの値を一緒に格納するた