복붙노트

[SCALA] 어떻게 선물 꼬리 재귀를 포함하는 함수를 어떻게해야합니까?

SCALA

어떻게 선물 꼬리 재귀를 포함하는 함수를 어떻게해야합니까?

내 스칼라 응용 프로그램에서, 나는 형의 미래 [T]의 결과를 반환하는 함수를 호출하는 기능을 가지고있다. 내 재귀 함수 호출에 매핑 된 결과를 전달해야합니다. 나는이 꼬리 재귀되고 싶어하지만,지도 (또는 flatMap)는 그렇게 할 수있는 능력을 파괴한다. 나는 "꼬리 위치에 재귀 호출 없습니다."오류가 발생합니다

다음은이 시나리오의 간단한 예입니다. 이것은 호출이 꼬리 재귀 될 수 있도록하는 방법을 수정할 수 있습니다 (AN Await.result과 선물의 혜택을 전복하지 않고 ())?

import scala.annotation.tailrec
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._

implicit val ec = scala.concurrent.ExecutionContext.global

object FactorialCalc {
  def factorial(n: Int): Future[Int] = {

    @tailrec
    def factorialAcc(acc: Int, n: Int): Future[Int] = {
      if (n <= 1) {
        Future.successful(acc)

      } else {
        val fNum = getFutureNumber(n)
        fNum.flatMap(num => factorialAcc(num * acc, num - 1))
      }
    }

    factorialAcc(1, n)
  }

  protected def getFutureNumber(n: Int) : Future[Int] = Future.successful(n)
}

Await.result(FactorialCalc.factorial(4), 5.seconds)

해결법

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

    1.내가 잘못 될 수도 있지만 함수는이 경우에 꼬리 재귀 할 필요는 없습니다.

    내가 잘못 될 수도 있지만 함수는이 경우에 꼬리 재귀 할 필요는 없습니다.

    꼬리 재귀는 우리가 재귀 함수를 사용하는 경우에는 스택을 소모하지하는 데 도움이됩니다. 귀하의 경우에는, 그러나, 우리는 실제로 일반적인 재귀 함수는 것 길에서 스택을 소모하지 않습니다.

    은 "재귀"호출이 실행 컨텍스트에서 일부 스레드에서 비동기 적으로 발생하기 때문입니다. 그래서 가능성이 재귀 호출도 첫 번째 통화와 동일한 스택에 상주 할 것입니다.

    factorialAcc 방법은 결국 비동기 적으로 "재귀"전화를 트리거 할 미래의 객체를 생성합니다. 그 후, 즉시 스택에서 팝됩니다.

    이 스택 재귀와 N에 비례하여 증가하지 않는 스택이 실제로되지 않도록, 그것은 일정한 크기로 대략 유지됩니다.

    당신은 쉽게 factorialAcc 방법의 어느 시점에서 예외를 던지고 스택 추적을 검사하여이를 확인할 수 있습니다.

    나는 더 읽기 스택 추적을 얻기 위해 프로그램을 재 작성 :

    object Main extends App {
      import scala.concurrent.{Await, Future}
      import scala.concurrent.duration._
    
      implicit val ec = scala.concurrent.ExecutionContext.global
    
      def factorialAcc(acc: Int, n: Int): Future[Int] = {
    
        if (n == 97)
          throw new Exception("n is 97")
    
        if (n <= 1) {
          Future.successful(acc)
    
        } else {
          val fNum = getFutureNumber(n)
          fNum.flatMap(num => factorialAcc(num * acc, num - 1))
        }
      }
    
    
      def factorial(n: Int): Future[Int] = {
          factorialAcc(1, n)
      }
    
      protected def getFutureNumber(n: Int) : Future[Int] = Future.successful(n)
    
      val r = Await.result(factorial(100), 5.seconds)
      println(r)
    
    }
    

    그리고 출력은 다음과 같습니다

    Exception in thread "main" java.lang.Exception: n is 97
    at test.Main$.factorialAcc(Main.scala:16)
    at test.Main$$anonfun$factorialAcc$1.apply(Main.scala:23)
    at test.Main$$anonfun$factorialAcc$1.apply(Main.scala:23)
    at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:278)
    at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:274)
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:29)
    at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
    

    그래서 당신은 스택이 짧은 실제로 볼 수 있습니다. 이 스택 재귀 인 경우에는 factorialAcc 방법 97 개 호출에 대해 볼 수 있어야합니다. 대신 하나를 참조하십시오.

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

    2.어떻게 대신 foldLeft 사용에 대한?

    어떻게 대신 foldLeft 사용에 대한?

    def factorial(n: Int): Future[Int] = future {
      (1 to n).foldLeft(1) { _ * _ }
    }
    
  3. ==============================

    3.여기에 또 다른 함수를 반환하는 미래를 호출하는 foldLeft 솔루션입니다.

    여기에 또 다른 함수를 반환하는 미래를 호출하는 foldLeft 솔루션입니다.

    def factorial(n: Int): Future[Int] =
      (1 to n).foldLeft(Future.successful(1)) {
        (f, n) => f.flatMap(a => getFutureNumber(n).map(b => a * b))
      }
    
    def getFutureNumber(n: Int) : Future[Int] = Future.successful(n)
    
  4. ==============================

    4.factorialAcc는 int를 반환 만 계승 기능의 미래에 포장합니다.

    factorialAcc는 int를 반환 만 계승 기능의 미래에 포장합니다.

    def factorial(n: Int): Future[Int] = {
    
        @tailrec
        def factorialAcc(acc: Int, n: Int): Int = {
          if (n <= 1) {
            acc
          } else {
            factorialAcc(n*acc,n-1)
          }
        }
    
        future {
          factorialAcc(1, n)
        }
    }
    

    아마 작동합니다.

  5. from https://stackoverflow.com/questions/16973838/how-do-i-make-a-function-involving-futures-tail-recursive by cc-by-sa and MIT license