[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.내가 잘못 될 수도 있지만 함수는이 경우에 꼬리 재귀 할 필요는 없습니다.
내가 잘못 될 수도 있지만 함수는이 경우에 꼬리 재귀 할 필요는 없습니다.
꼬리 재귀는 우리가 재귀 함수를 사용하는 경우에는 스택을 소모하지하는 데 도움이됩니다. 귀하의 경우에는, 그러나, 우리는 실제로 일반적인 재귀 함수는 것 길에서 스택을 소모하지 않습니다.
은 "재귀"호출이 실행 컨텍스트에서 일부 스레드에서 비동기 적으로 발생하기 때문입니다. 그래서 가능성이 재귀 호출도 첫 번째 통화와 동일한 스택에 상주 할 것입니다.
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.어떻게 대신 foldLeft 사용에 대한?
어떻게 대신 foldLeft 사용에 대한?
def factorial(n: Int): Future[Int] = future { (1 to n).foldLeft(1) { _ * _ } }
-
==============================
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.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) } }
아마 작동합니다.
from https://stackoverflow.com/questions/16973838/how-do-i-make-a-function-involving-futures-tail-recursive by cc-by-sa and MIT license
'SCALA' 카테고리의 다른 글
[SCALA] 위 유형 경계에서 작동하지 않는 추상 형식을 무시 케이크 패턴 (0) | 2019.11.10 |
---|---|
[SCALA] 평균으로 누락 된 값을 교체 - 불꽃 Dataframe을 (0) | 2019.11.10 |
[SCALA] 업로드 된 파일의 경로를 얻는 방법 (0) | 2019.11.10 |
[SCALA] 왜 SparkContext.textFile의 파티션 매개 변수는 적용되지 않습니다? (0) | 2019.11.09 |
[SCALA] 스파크 Dataframe하십시오 인덱스 열을 추가하는 방법 : 아카 데이터 색인 분산 (0) | 2019.11.09 |