내 첫 번째 선택은 일반적으로 재귀를 사용하는 것입니다. 다소 덜 컴팩트하고 잠재적으로 더 빠르며 (확실히 더 느리지 않음) 조기 종료시 로직을 더 명확하게 만들 수 있습니다. 이 경우 약간 어색한 중첩 정의가 필요합니다.
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)
}
두 번째 선택은를 사용 return
하는 것입니다. 다른 모든 것을 그대로 유지하고 접기 만하여 def
반환 할 항목을 가지기 만하면됩니다.이 경우에는 이미 메서드가 있습니다.
def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
Some(nums.foldLeft(0){ (n,x) =>
if ((n % 2) != 0) return None
n+x
})
}
이 특별한 경우에는 재귀보다 훨씬 더 간결합니다 (반복 가능 / 반복 변환을 수행해야했기 때문에 재귀에 특히 불행 해졌지만). 비정상적인 제어 흐름은 다른 모든 것이 동일 할 때 피해야하는 것이지만 여기서는 그렇지 않습니다. 귀중한 경우 사용해도 해가 없습니다.
이 작업을 자주 수행하고 어딘가에서 메서드 중간에 원하는 경우 (반환 만 사용할 수는 없음) 예외 처리를 사용하여 로컬이 아닌 제어 흐름을 생성 할 수 있습니다. 즉, 결국 그것이 좋은 점이며 오류 처리가 유용한 유일한 시간은 아닙니다. 유일한 트릭은 스택 트레이스 (정말 느린) 생성을 피하는 것입니다. 트레이 트 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
전체 메서드를 래핑하는 것이 아니라 어디에나 넣을 수 있습니다.
다음으로 폴드를 다시 구현하여 (나 자신이나이를 수행하는 라이브러리를 찾아서) 조기 종료 신호를 보낼 수 있습니다. 이를 수행하는 두 가지 자연스러운 방법은 값을 전파하지 않고 값을 Option
포함하는 것입니다. 여기서 None
종료를 의미합니다. 또는 완료를 알리는 두 번째 표시기 기능을 사용합니다. Kim Stebel이 보여준 Scalaz lazy fold는 이미 첫 번째 경우를 다루고 있으므로 두 번째 사례를 보여 드리겠습니다 (변경 가능한 구현 포함).
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
하지만 그렇지 않으며 손으로 수행하는 데 필요한 추가 작업으로 인해 여기서 사용하는 것은 어리석은 옵션입니다.)