私はまだ Scala の初心者ですが、現在、次のコードを Scala で実装する方法を探しています。
@Override
public void store(InputStream source, String destination, long size) {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(size);
final PutObjectRequest request = new PutObjectRequest(
this.configuration.getBucket(), destination, source, metadata);
new RetryableService(3) {
@Override
public void call() throws Exception {
getClient().putObject(request);
}
};
}
RetryableService が実装するのと同じ機能を Scala で実装する最良の方法は何でしょうか?
それは基本的に電話メソッドを N 回実行し、すべて失敗した場合は例外が発生し、成功した場合は次に進みます。これは何も返しませんが、値を返すことができる別のバージョンがあります (つまり、Java で 2 つのクラスがあります)。Scala では単一のクラス/関数で済むと思います。
何か案は?
編集
Java での現在の実装は次のとおりです。
public abstract class RetryableService {
private static final JobsLogger log = JobsLogger
.getLogger(RetryableService.class);
private int times;
public RetryableService() {
this(3);
}
public RetryableService(int times) {
this.times = times;
this.run();
}
private void run() {
RuntimeException lastExceptionParent = null;
int x = 0;
for (; x < this.times; x++) {
try {
this.call();
lastExceptionParent = null;
break;
} catch (Exception e) {
lastExceptionParent = new RuntimeException(e);
log.errorWithoutNotice( e, "Try %d caused exception %s", x, e.getMessage() );
try {
Thread.sleep( 5000 );
} catch (InterruptedException e1) {
log.errorWithoutNotice( e1, "Sleep inside try %d caused exception %s", x, e1.getMessage() );
}
}
}
try {
this.ensure();
} catch (Exception e) {
log.error(e, "Failed while ensure inside RetryableService");
}
if ( lastExceptionParent != null ) {
throw new IllegalStateException( String.format( "Failed on try %d of %s", x, this ), lastExceptionParent);
}
}
public void ensure() throws Exception {
// blank implementation
}
public abstract void call() throws Exception;
}
ベストアンサー1
再帰 +
ファーストクラス関数
の名前によるパラメータ == 素晴らしい。
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e =>
if (n > 1) retry(n - 1)(fn)
else throw e
}
}
使い方は次のようになります:
retry(3) {
// insert code that may fail here
}
編集: インスピレーションを受けた若干のバリエーション@セメルの答えです。コードが 1 行減りました :-)
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e if n > 1 =>
retry(n - 1)(fn)
}
}
再度編集: 再帰はスタック トレースに複数の呼び出しを追加するため、気になりました。何らかの理由で、コンパイラは catch ハンドラ内の末尾再帰を最適化できませんでした。ただし、catch ハンドラ内ではない末尾再帰は、最適化は問題なく行われます :-)
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
val r = try { Some(fn) } catch { case e: Exception if n > 1 => None }
r match {
case Some(x) => x
case None => retry(n - 1)(fn)
}
}
もう一度編集: どうやら、この回答に戻って代替案を追加し続けるのが趣味になりそうです。 を使用するよりも少し簡単な末尾再帰バージョンを次に示しますがOption
、 を使ってreturn
関数を短絡するのは Scala の慣用的な方法ではありません。
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
try {
return fn
} catch {
case e if n > 1 => // ignore
}
retry(n - 1)(fn)
}
Scala 2.10 アップデート私の趣味として、私は時々この回答を見直します。Scala 2.10が導入されました試すは、末尾再帰的な方法で再試行を実装するクリーンな方法を提供します。
// Returning T, throwing the exception on failure
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
util.Try { fn } match {
case util.Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case util.Failure(e) => throw e
}
}
// Returning a Try[T] wrapper
@annotation.tailrec
def retry[T](n: Int)(fn: => T): util.Try[T] = {
util.Try { fn } match {
case util.Failure(_) if n > 1 => retry(n - 1)(fn)
case fn => fn
}
}