Scalaz (35) - Free: 연산 - Trampoline, NO to StackOverflow 오류
1 ef zipIndex[A](xa: List[A]): List[(Int,A)] =
2 xa.foldLeft(State.state[Int,List[(Int,A)]](List()))( 3 (acc,a) => for { 4 xn <- acc 5 s <- get[Int] 6 _ <- put[Int](s+1) 7 } yield ((s,a) :: xn) 8 ).eval(1).reverse //> zipIndex: [A](xa: List[A])List[(Int, A)]
9
10 zipIndex(1 |-> 10) //> res6: List[(Int, Int)] = List((1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,9), (10,10))
11 zipIndex(1 |-> 10000) //> java.lang.StackOverflowError
이 예 에서 우 리 는 State Monad 를 사용 했다.우 리 는 for - comprehension 이 바로 flatMap 체인 이 고 재 귀 알고리즘 이라는 것 을 알 기 때문에 zipIndex 가 큰 List 를 대상 으로 할 때 StackOverflow Error 가 발생 했다.
우 리 는 Trampoline 을 사용 하면 힙 을 stack 으로 바 꾸 고 데이터 구 조 를 옮 겨 다 니 며 재 귀 연산 을 대체 하여 운행 안전 을 실현 할 수 있다 고 언급 한 적 이 있다.그럼 트 램 폴 린 이 뭐 죠?
1 sealed trait Trampoline[+A] 2 case class Done[A](a: A) extends Trampoline[A] 3 case class More[A](k: () => Trampoline[A]) extends Trampoline[A]
Trampoline 은 일종 의 데이터 구조 다.이 는 두 가지 상태 가 있 습 니 다. Done (a: A) 대표 구조 에 A 값 이 저장 되 어 있 습 니 다. More (k: () = > Trampoline [A] 대표 구조 에 저 장 된 것 은 Function 0 함수 이 고 연산 Trampoline [A] 입 니 다.
우 리 는 먼저 재 귀 알고리즘 예 를 시험 해 보 자.
1 def isEven(xa: List[Int]): Boolean =
2 xa match { 3 case Nil => true
4 case h :: t => isOdd(t) 5 } //> isEven: (xa: List[Int])Boolean
6 def isOdd(xa: List[Int]): Boolean =
7 xa match { 8 case Nil => false
9 case h :: t => isEven(t) 10 } //> isOdd: (xa: List[Int])Boolean
11
12 isOdd(0 |-> 100) //> res0: Boolean = true
13 isEven(0 |-> 10000) //> java.lang.StackOverflowError
isEven 과 isOdd 라 는 두 함수 가 서로 재 귀적 으로 호출 되 는 것 을 볼 수 있 습 니 다. 마지막 으로 큰 List 를 사용 하면 StackOverflow Error 가 발생 합 니 다.
함수 isEven 과 isOdd 의 반환 구조 유형 을 다시 조정 합 니 다. Boolean 에서 Trampoline 으로 바 꾸 면 결과 값 을 되 돌려 주 는 것 에서 데이터 구 조 를 되 돌려 주 는 것 을 의미 합 니 다.
1 def even(xa: List[Int]): Trampoline[Boolean] =
2 xa match { 3 case Nil => Done(true) 4 case h :: t => More(() => odd(t)) 5 } //> even: (xa: List[Int])Exercises.trampoline.Trampoline[Boolean]
6 def odd(xa: List[Int]): Trampoline[Boolean] =
7 xa match { 8 case Nil => Done(false) 9 case h :: t => More(() => even(t)) 10 } //> odd: (xa: List[Int])Exercises.trampoline.Trampoline[Boolean]
11
12 even(1 |-> 123001) //> res0: Exercises.trampoline.Trampoline[Boolean] = More(<function0>)
현재 우 리 는 힙 에 123001 개의 요 소 를 저장 한 데이터 구조 More (< function0 >) 를 얻 었 다.이것 은 메모리 힙 에 저장 하 는 과정 으로 실질 적 인 연산 이 없다.현재 우 리 는 이 되 돌아 오 는 구 조 를 옮 겨 다 니 며 구조 중의 function 0 을 하나씩 실행 하 는 방법 이 필요 합 니 다.
1 sealed trait Trampoline[+A] { 2 final def runT: A =
3 this match { 4 case Done(a) => a 5 case More(k) => k().runT 6 } 7 } 8
9 even(1 |-> 123001).runT //> res0: Boolean = false
이 runT 는 끝 재 귀 (Tail Call Elimination TCE) 알고리즘 이기 때문에 StackOverflow Error 가 나타 나 지 않 았 습 니 다.
실제로 scalaz 도 Trampoline 유형: scalaz / Free. scala 를 제공 합 니 다.
/** A computation that can be stepped through, suspended, and paused */ type Trampoline[A] = Free[Function0, A] ... object Trampoline extends TrampolineInstances { def done[A](a: A): Trampoline[A] = Free.Return[Function0,A](a) def delay[A](a: => A): Trampoline[A] = suspend(done(a)) def suspend[A](a: => Trampoline[A]): Trampoline[A] = Free.Suspend[Function0, A](() => a) }
Trampoline 은 바로 Free [S, A] 의 특례 이다.S = = Function 0, 또는 Trampoline 은 Free 가 Function 0 을 대상 으로 생 성 된 Monad 입 니 다. 우 리 는 free. Return 과 Free. Suspend 로 Done 과 More 를 실현 할 수 있 기 때 문 입 니 다.우 리 는 scalaz 의 Trampoline 을 even, odd 함수 에 사용 할 수 있 습 니 다.
1 import scalaz.Free.Trampoline 2 def even(xa: List[Int]): Trampoline[Boolean] =
3 xa match { 4 case Nil => Trampoline.done(true) 5 case h :: t => Trampoline.suspend(odd(t)) 6 } //> even: (xa: List[Int])scalaz.Free.Trampoline[Boolean]
7 def odd(xa: List[Int]): Trampoline[Boolean] =
8 xa match { 9 case Nil => Trampoline.done(false) 10 case h :: t => Trampoline.suspend(even(t)) 11 } //> odd: (xa: List[Int])scalaz.Free.Trampoline[Boolean]
12
13 even(1 |-> 123001).run //> res0: Boolean = false
문법 은 우리 가 정의 한 Trampoline 과 차이 가 많 지 않다.그러면 우 리 는 Trampoline 을 위의 어떤 zipIndex 함수 에 사용 하여 StackOverflow Error 문 제 를 해결 할 수 있 습 니까?zipIndex 에서 문 제 를 일 으 킨 Monad 는 State Monad 입 니 다. 우 리 는 State. lift 로 State [S, A 를 State T [Trampoline, S, A] 로 승격 할 수 있 습 니 다. 먼저 이 lift 함수: scalaz / State T. scala 를 보십시오.
def lift[M[_]: Applicative]: IndexedStateT[({type λ[α]=M[F[α]]})#λ, S1, S2, A] = new IndexedStateT[({type λ[α]=M[F[α]]})#λ, S1, S2, A] { def apply(initial: S1): M[F[(S2, A)]] = Applicative[M].point(self(initial)) }
이 함수 의 기능 은 State. lift [Trampoline] > > State T [Tarmpoline, S, A] 와 같 습 니 다. 다른 간단 한 예 를 먼저 보 세 요.
1 def incr: State[Int, Int] = State {s => (s+1, s)}//> incr: => scalaz.State[Int,Int]
2 incr.replicateM(10000).eval(0) take 10 //> java.lang.StackOverflowError
3
4 import scalaz.Free.Trampoline 5 incr.lift[Trampoline].replicateM(100000).eval(0).run.take(10) 6 //> res0: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
위의 예 에서 도 State Monad 를 사 용 했 습 니 다. 함수 incr 는 State 로 되 돌 아 왔 습 니 다. 이때 replicateM (10000). eval (0) 으로 10000 개 State 를 반복 적 으로 연산 할 때 StackOverflow Error 가 발생 했 습 니 다. 우 리 는 lift 를 따라 incr 를 State T [Trampoline, S, A] 로 되 돌 렸 습 니 다. 이때 replicateM (10000). eval (0) 의 역할 은 구조 전환 (State. apply: Trampoline [(S, A)]) 하 는 것 입 니 다., 다시 Trampoline. run 을 Interpreter 스 트 리밍 구조 로 연산 합 니 다. lift 로 Trampoline 을 승격 한 후 StackOverflow Error 를 해결 하 였 습 니 다.
zipIndex 함 수 를 조정 해 보 겠 습 니 다.
1 def safeZipIndex[A](xa: List[A]): List[(Int,A)] =
2 (xa.foldLeft(State.state[Int,List[(Int,A)]](List()))( 3 (acc,a) => for { 4 xn <- acc 5 s <- get[Int] 6 _ <- put(s + 1) 7 } yield (s,a) :: xn 8 ).lift[Trampoline]).eval(1).run.reverse //> safeZipIndex: [A](xa: List[A])List[(Int, A)]
9
10 safeZipIndex(1 |-> 1000).take(10) //> res2: List[(Int, Int)] = List((1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,9), (10,10))
겉 으로 는 결과 가 맞 는 것 같 습 니 다. 좀 큰 List 를 시도 해 보 세 요.
1 safeZipIndex(1 |-> 10000).take(10) //> java.lang.StackOverflowError 2 //| at scalaz.IndexedStateT$$anonfun$flatMap$1.apply(StateT.scala:62) 3 //| at scalaz.IndexedStateT$$anon$10.apply(StateT.scala:95) 4 //| at scalaz.IndexedStateT$$anonfun$flatMap$1.apply(StateT.scala:62)
5 ... 6
아니면 StackOverflow Error 입 니까? 잘못된 힌트 는 State. flatMap 에 의 한 것 입 니 다. 늦 은 점 은 incr 의 원리 에 따라 foldLeft 연산 단계 의 결 과 를 나 누 어 분석 해 야 할 것 같 습 니 다.
이상 에서 우 리 는 Trampoline 이 연속 연산 을 데이터 구 조 를 만 들 고 힙 메모리 로 stack 을 바 꾸 어 재 귀 알고리즘 운행 의 안전 을 보장 할 수 있다 는 것 을 증명 했다. Trampoline 은 Free 의 특례 이기 때문에 Free Interpreter 도 재 귀 알고리즘 의 안전 한 운행 을 보장 할 수 있다. 지금 은 이러한 결론 을 얻 을 수 있다. FP 는 Monadic Programming 이 고 Monad 로 프로 그래 밍 하 는 것 이다.우 리 는 가능 한 한 Free 로 Monad 를 생 성하 고 Free 로 프로 그래 밍 을 해서 FP 프로그램의 신뢰성 을 확보 해 야 한다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.