복붙노트

[SCALA] 접이식 조기 중단

SCALA

접이식 조기 중단

일찍 배를 종료하는 가장 좋은 방법은 무엇입니까? 간단한 예를 들어, 내가의 Iterable의 숫자를 합계를 상상하지만, 내가 기대 아니에요 뭔가가 발생하는 경우 (예를 들어 홀수) 나는 종료 할 수 있습니다. 이것은 제 근사

def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
  nums.foldLeft (Some(0): Option[Int]) {
    case (Some(s), n) if n % 2 == 0 => Some(s + n)
    case _ => None
  }
}

그러나,이 솔루션은 매우 추한 (같이 나는 .foreach와 수익을 한 경우에, -가 훨씬 깨끗하고 선명한 것) 모두의 최악은, 그것이 아닌 짝수가 발생하는 경우에도 전체 반복자를 통과 .

그래서 조기 종료이 같은 배를 작성하는 가장 좋은 방법이 있을까요? 난 그냥 가서 재귀이 쓰기, 또는 더 허용 방법이해야 하는가?

해결법

  1. ==============================

    1.나의 첫 번째 선택은 일반적으로 재귀를 사용하는 것입니다. 그것은 단지 적당히 적은 소형, 잠재적으로 더 빠른입니다 (확실히 더 느린), 및 조기 종료에 논리가 더 명확하게 할 수 없습니다. 이 경우에는 조금 어색 중첩 인증 된 정의가 필요합니다 :

    나의 첫 번째 선택은 일반적으로 재귀를 사용하는 것입니다. 그것은 단지 적당히 적은 소형, 잠재적으로 더 빠른입니다 (확실히 더 느린), 및 조기 종료에 논리가 더 명확하게 할 수 없습니다. 이 경우에는 조금 어색 중첩 인증 된 정의가 필요합니다 :

    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)
    }
    

    내 두 번째 선택이 다른 모든 것을 그대로 유지하고 당신은 단지 당신이에서 반환하는 무언가가 있도록 데프의 배를 감싸 필요로 복귀를 사용하는 것 -이 경우에는, 당신은 이미, 방법이있다 :

    def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
      Some(nums.foldLeft(0){ (n,x) =>
        if ((n % 2) != 0) return None
        n+x
      })
    }
    

    (우리는 반복 가능한 / 반복자 변환을해야했다 이후 우리는 재귀 특히 불운 가지고 있지만)하는이 특별한 경우에 더 많은 소형 재귀보다. 불안해 제어 흐름은 모든 사람이 동일 할 때 피하기 위해 뭔가이지만, 여기가 아니다. 그것은 가치의 경우에 사용에 전혀 해를 끼치 지 않습니다.

    나는 종종 이렇게 어딘가에 방법의 중간 이내에 원하는 경우 (그래서 난 그냥 반환을 사용할 수없는), 아마 예외 처리 로컬이 아닌 제어 흐름을 생성을 사용합니다. 즉, 잘, 그리고 오류 처리가 유용의 유일한 시간이 아니다 무엇인지, 결국입니다. 유일한 트릭은 (정말 느린) 스택 트레이스를 발생하지 않도록하는 것입니다, 그리고 특성 NoStackTrace와 자식 특성 ControlThrowable가 이미 당신을 위해 그렇게 때문에 그것은 간단합니다. 스칼라는 이미 (는 배 내부의 반환을 구현하는 방법을 그건 사실!) 내부적으로 사용합니다. (사람이 그것을 고칠 수 있지만, 중첩 될 수 없습니다)의 우리 자신을 만들어 보자 :

    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
      })
    }
    

    여기에 물론 수익이 더 사용하지만, 당신은 단지 전체 방법을 포장하지, 어디서든 바로 가기를 넣을 수 있습니다.

    나를 위해 줄에 다음 배를 다시 구현하는 것입니다 (중 자신 또는 그것을 수행하는 라이브러리를 찾을)가 조기 종료 신호 수 있도록합니다. 이 일을 두 자연적인 방법은 값하지만 아무도 종료를 의미없는 값을 포함하는 옵션을 전파하지 않을 수 있습니다; 또는 완료 신호를 제 지시 함수를 사용한다. 김 Stebel에 의해 표시되는 Scalaz 게으른 배는 이미 첫 번째 경우를 포함, 그래서 나는 (변경 가능한 구현) 두 번째를 보여줄 것이다 :

    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)(_ + _)
    

    (당신이 등 재귀, 반환, 게으름에 의해 종료를 구현 여부는 당신에게 달려 있습니다.)

    나는 그 주요 합리적인 변종을 포함 생각; 거기에 몇 가지 다른 옵션도 있습니다,하지만 난 하나이 경우 사용할 이유를 모르겠어요. (그것이 findOrPrevious이 있다면 그 자체가 잘 작동 반복자하지만, 그렇지 않습니다, 그리고 추가 작업은 손으로 그것을 여기에 사용하는 바보 같은 선택을하게 그렇게하는 데 많은 시간이 소요됩니다.)

  2. ==============================

    2.당신이 설명하는 시나리오 (원하지 않는 조건에 따라 출구)는 takeWhile 방법에 대한 좋은 유스 케이스처럼 보인다. 그것은 본질적으로 필터링되어 있지만 조건을 만족하지 않는 요소가 발생할 때 끝나야합니다.

    당신이 설명하는 시나리오 (원하지 않는 조건에 따라 출구)는 takeWhile 방법에 대한 좋은 유스 케이스처럼 보인다. 그것은 본질적으로 필터링되어 있지만 조건을 만족하지 않는 요소가 발생할 때 끝나야합니다.

    예를 들면 :

    val list = List(2,4,6,8,6,4,2,5,3,2)
    list.takeWhile(_ % 2 == 0) //result is List(2,4,6,8,6,4,2)
    

    이것은 너무 반복자 / 반복 가능 객체를 위해 잘 작동합니다. 이 솔루션 난 당신을위한 제안 "짝수의 합이 있지만, 홀수에 휴식은":

    list.iterator.takeWhile(_ % 2 == 0).foldLeft(...)
    

    그리고 그냥 홀수 안타 일단은 시간을 낭비 아니에요 것을 증명하기 위해 ...

    scala> val list = List(2,4,5,6,8)
    list: List[Int] = List(2, 4, 5, 6, 8)
    
    scala> def condition(i: Int) = {
         |   println("processing " + i)
         |   i % 2 == 0
         | }
    condition: (i: Int)Boolean
    
    scala> list.iterator.takeWhile(condition _).sum
    processing 2
    processing 4
    processing 5
    res4: Int = 6
    
  3. ==============================

    3.당신은 당신이 scalaz에 foldRight의 게으른 버전을 사용하여 기능적인 스타일로 원하는 것을 할 수 있습니다. 더 깊이 설명은이 블로그 게시물을 참조하십시오. 이 솔루션은 스트림을 사용하는 동안, 당신은 iterable.toStream 효율적으로 스트림으로 반복 가능을 변환 할 수 있습니다.

    당신은 당신이 scalaz에 foldRight의 게으른 버전을 사용하여 기능적인 스타일로 원하는 것을 할 수 있습니다. 더 깊이 설명은이 블로그 게시물을 참조하십시오. 이 솔루션은 스트림을 사용하는 동안, 당신은 iterable.toStream 효율적으로 스트림으로 반복 가능을 변환 할 수 있습니다.

    import scalaz._
    import Scalaz._
    
    val str = Stream(2,1,2,2,2,2,2,2,2)
    var i = 0 //only here for testing
    val r = str.foldr(Some(0):Option[Int])((n,s) => {
      println(i)
      i+=1
      if (n % 2 == 0) s.map(n+) else None
    })
    

    이 단지 인쇄

    0
    1
    

    분명히하는 익명 함수 만 배라는 것을 보여준다 (즉, 그것은 홀수 만날 때까지). 즉 DEF foldr되고 그 서명 (스트림의 경우) foldr의 정의에 의한 [B] (b : B) (F (INT, => B) => B) (암시 R : scalaz.Foldable [ 트림]) : B. 참고 익명 함수 번째 인수로서 이름 파라미터에 의해 필요하지 않으므로 더 평가 될 필요가있다.

    BTW, 당신은 여전히 ​​OP의 패턴 매칭 솔루션이 쓸 수 있습니다,하지만 난 경우 / 다른 사람 찾아 더 우아한지도.

  4. ==============================

    4.음, 스칼라가 아닌 지역의 반환을 허용하지 않습니다. 이것은 좋은 스타일 여부에 대한 다른 의견이있다.

    음, 스칼라가 아닌 지역의 반환을 허용하지 않습니다. 이것은 좋은 스타일 여부에 대한 다른 의견이있다.

    scala> def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
         |   nums.foldLeft (Some(0): Option[Int]) {
         |     case (None, _) => return None
         |     case (Some(s), n) if n % 2 == 0 => Some(s + n)
         |     case (Some(_), _) => None
         |   }
         | }
    sumEvenNumbers: (nums: Iterable[Int])Option[Int]
    
    scala> sumEvenNumbers(2 to 10)
    res8: Option[Int] = None
    
    scala> sumEvenNumbers(2 to 10 by 2)
    res9: Option[Int] = Some(30)
    

    편집하다:

    이 특정한 경우에, @Arjan 제안, 당신은 할 수 있습니다 :

    def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
      nums.foldLeft (Some(0): Option[Int]) {
        case (Some(s), n) if n % 2 == 0 => Some(s + n)
        case _ => return None
      }
    }
    
  5. ==============================

    5.고양이 (벡터, 목록, 스트림, ...에 대한) 단락을 수행 foldM라는 메소드가 있습니다.

    고양이 (벡터, 목록, 스트림, ...에 대한) 단락을 수행 foldM라는 메소드가 있습니다.

    다음과 같이 작동합니다 :

    def sumEvenNumbers(nums: Stream[Int]): Option[Long] = {
      import cats.implicits._
      nums.foldM(0L) {
        case (acc, c) if c % 2 == 0 => Some(acc + c)
        case _ => None
      }
    }
    

    즉시 컬렉션의 요소 중 하나가도 아니다, 그것은 반환합니다.

  6. ==============================

    6.@Rex 커 당신의 대답은 나에게 도움이,하지만 난 하나를 사용하도록 조정할 필요

    @Rex 커 당신의 대답은 나에게 도움이,하지만 난 하나를 사용하도록 조정할 필요

      
      def foldOrFail[A,B,C,D](map: B => Either[D, C])(merge: (A, C) => A)(initial: A)(it: Iterable[B]): Either[D, A] = {
        val ii= it.iterator
        var b= initial
        while (ii.hasNext) {
          val x= ii.next
          map(x) match {
            case Left(error) => return Left(error)
            case Right(d) => b= merge(b, d)
          }
        }
        Right(b)
      }
    
  7. ==============================

    7.당신은 임시 VAR를 사용 takeWhile를 사용하여 시도 할 수 있습니다. 여기 버전입니다.

    당신은 임시 VAR를 사용 takeWhile를 사용하여 시도 할 수 있습니다. 여기 버전입니다.

      var continue = true
    
      // sample stream of 2's and then a stream of 3's.
    
      val evenSum = (Stream.fill(10)(2) ++ Stream.fill(10)(3)).takeWhile(_ => continue)
        .foldLeft(Option[Int](0)){
    
        case (result,i) if i%2 != 0 =>
              continue = false;
              // return whatever is appropriate either the accumulated sum or None.
              result
        case (optionSum,i) => optionSum.map( _ + i)
    
      }
    

    evenSum이 경우 일부 (20)이어야한다.

  8. ==============================

    8.당신은 호출 코드에서 처리, 당신의 종료 기준을 발생에 따라 잘 선택 예외를 던질 수 있습니다.

    당신은 호출 코드에서 처리, 당신의 종료 기준을 발생에 따라 잘 선택 예외를 던질 수 있습니다.

  9. ==============================

    9.더 아름다운 해결책은 범위를 사용하는 것입니다 :

    더 아름다운 해결책은 범위를 사용하는 것입니다 :

    val (l, r) = numbers.span(_ % 2 == 0)
    if(r.isEmpty) Some(l.sum)
    else None
    

    모든 숫자도 있다면 ...하지만 그것은 목록을 두 번 통과

  10. ==============================

    10.그냥 "학술"이유 (대한 :

    그냥 "학술"이유 (대한 :

    var headers = Source.fromFile(file).getLines().next().split(",")
    var closeHeaderIdx = headers.takeWhile { s => !"Close".equals(s) }.foldLeft(0)((i, S) => i+1)
    

    이해야 다음 번 걸리지 만 그것은 좋은 일 라이너입니다. "닫기"경우가 반환됩니다 찾을 수 없음

    headers.size
    

    또 다른 (더 나은)이 하나입니다 :

    var headers = Source.fromFile(file).getLines().next().split(",").toList
    var closeHeaderIdx = headers.indexOf("Close")
    
  11. from https://stackoverflow.com/questions/12892701/abort-early-in-a-fold by cc-by-sa and MIT license