私の最初の選択は通常、再帰を使用することです。それは適度にコンパクトではなく、潜在的に高速であり(確かに低速ではありません)、早期終了でロジックをより明確にすることができます。この場合、ネストされたdefが必要ですが、これは少し厄介です。
def sumEvenNumbers(nums: Iterable[Int]) = {
def sumEven(it: Iterator[Int], n: Int): Option[Int] = {
if (it.hasNext) {
val x = it.next
if ((x % 2) == 0) sumEven(it, n+x) else None
}
else Some(n)
}
sumEven(nums.iterator, 0)
}
私の2番目の選択肢は、を使用するreturn
ことです。これにより、他のすべてがそのまま保持され、折り目をラップするだけで、def
何かを返すことができます。この場合、すでにメソッドがあります。
def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
Some(nums.foldLeft(0){ (n,x) =>
if ((n % 2) != 0) return None
n+x
})
}
この特定のケースでは、再帰よりもはるかにコンパクトです(ただし、反復可能/反復子変換を実行する必要があったため、再帰では特に不運になりました)。びくびくした制御フローは、他のすべてが等しい場合に避けるべきものですが、ここではそうではありません。価値のある場合に使用しても害はありません。
これを頻繁に行っていて、どこかのメソッドの途中でそれが必要な場合(したがって、returnを使用することはできませんでした)、おそらく例外処理を使用して非ローカル制御フローを生成します。つまり、結局のところ、それが得意なことであり、エラー処理だけがそれが役立つときではありません。唯一の秘訣は、スタックトレース(非常に遅い)の生成を回避することです。これは、トレイトNoStackTrace
とその子トレイトがControlThrowable
すでにそれを行っているため、簡単です。Scalaはすでにこれを内部で使用しています(実際、これがフォールド内からのリターンを実装する方法です!)。自分で作成しましょう(ネストすることはできませんが、修正することはできます):
import scala.util.control.ControlThrowable
case class Returned[A](value: A) extends ControlThrowable {}
def shortcut[A](a: => A) = try { a } catch { case Returned(v) => v }
def sumEvenNumbers(nums: Iterable[Int]) = shortcut{
Option(nums.foldLeft(0){ (n,x) =>
if ((x % 2) != 0) throw Returned(None)
n+x
})
}
ここではもちろん使用するreturn
方が良いですが、置くことができることに注意してくださいshortcut
、メソッド全体をラップするだけでなく、どこにでもできる。
次に私が行うのは、foldを再実装して(自分自身またはそれを実行するライブラリを見つける)、早期終了の合図を出すことです。これを行う2つの自然な方法は、値を伝播するのではなく、終了を意味Option
する値を含むNone
ことです。または、完了を通知する2番目のインジケーター機能を使用します。Kim Stebelによって示されたScalazレイジーフォールドは、すでに最初のケースをカバーしているので、2番目のケースを示します(変更可能な実装を使用)。
def foldOrFail[A,B](it: Iterable[A])(zero: B)(fail: A => Boolean)(f: (B,A) => B): Option[B] = {
val ii = it.iterator
var b = zero
while (ii.hasNext) {
val x = ii.next
if (fail(x)) return None
b = f(b,x)
}
Some(b)
}
def sumEvenNumbers(nums: Iterable[Int]) = foldOrFail(nums)(0)(_ % 2 != 0)(_ + _)
(再帰、戻り、怠惰などによる終了を実装するかどうかはあなた次第です。)
私はそれが主な合理的な変種をカバーしていると思います。他にもいくつかのオプションがありますが、この場合になぜそれらを使用するのかわかりません。(Iterator
それ自体があればうまく機能しますが、そうではありfindOrPrevious
ません。手作業でそれを行うために余分な作業が必要になるため、ここで使用するのはばかげたオプションになります。)