[SCALA] 스칼라에서 memoize하는 일반적인 방법이 있나요?
SCALA스칼라에서 memoize하는 일반적인 방법이 있나요?
나는 이것을 memoize 싶었 :
def fib(n: Int) = if(n <= 1) 1 else fib(n-1) + fib(n-2)
println(fib(100)) // times out
내가 쓴이 놀라 울 정도로 컴파일하고 작품 그래서 (나는 FIB 자체를 참조 선언에 있기 때문에 놀라게하고있다)
case class Memo[A,B](f: A => B) extends (A => B) {
private val cache = mutable.Map.empty[A, B]
def apply(x: A) = cache getOrElseUpdate (x, f(x))
}
val fib: Memo[Int, BigInt] = Memo {
case 0 => 0
case 1 => 1
case n => fib(n-1) + fib(n-2)
}
println(fib(100)) // prints 100th fibonacci number instantly
내가 데프의 FIB의 내부를 선언 할 때, 나는 컴파일러 오류가 발생합니다 :
def foo(n: Int) = {
val fib: Memo[Int, BigInt] = Memo {
case 0 => 0
case 1 => 1
case n => fib(n-1) + fib(n-2)
}
fib(n)
}
위 오류를 컴파일에 실패 : 전방 참조 값의 FIB의 정의를 통해 확장 는 n => FIB (N-1) + FIB (N-2)
다음 DEF 클래스 / 객체 범위 작동에 실패하지만, 외부에서 내부에 왜 발에 FIB를 선언 하는가?
나는 데프 범위에서 재귀 memoized 함수를 선언 할 수 있습니다 이유를 명확히하기 위해 - 여기 부분 집합 합 문제에 대한 내 솔루션입니다 :
/**
* Subset sum algorithm - can we achieve sum t using elements from s?
*
* @param s set of integers
* @param t target
* @return true iff there exists a subset of s that sums to t
*/
def subsetSum(s: Seq[Int], t: Int): Boolean = {
val max = s.scanLeft(0)((sum, i) => (sum + i) max sum) //max(i) = largest sum achievable from first i elements
val min = s.scanLeft(0)((sum, i) => (sum + i) min sum) //min(i) = smallest sum achievable from first i elements
val dp: Memo[(Int, Int), Boolean] = Memo { // dp(i,x) = can we achieve x using the first i elements?
case (_, 0) => true // 0 can always be achieved using empty set
case (0, _) => false // if empty set, non-zero cannot be achieved
case (i, x) if min(i) <= x && x <= max(i) => dp(i-1, x - s(i-1)) || dp(i-1, x) // try with/without s(i-1)
case _ => false // outside range otherwise
}
dp(s.length, t)
}
해결법
-
==============================
1.나는 스칼라를 사용하여 memoize 수있는 더 좋은 방법을 발견 :
나는 스칼라를 사용하여 memoize 수있는 더 좋은 방법을 발견 :
def memoize[I, O](f: I => O): I => O = new mutable.HashMap[I, O]() { override def apply(key: I) = getOrElseUpdate(key, f(key)) }
다음과 같이 이제 피보나치을 작성할 수 있습니다 :
lazy val fib: Int => BigInt = memoize { case 0 => 0 case 1 => 1 case n => fib(n-1) + fib(n-2) }
여기에 여러 인수합니다 (기능 선택) 하나입니다 :
lazy val c: ((Int, Int)) => BigInt = memoize { case (_, 0) => 1 case (n, r) if r > n/2 => c(n, n - r) case (n, r) => c(n - 1, r - 1) + c(n - 1, r) }
그리고 여기에 집합 합 문제는 다음과 같습니다
// is there a subset of s which has sum = t def isSubsetSumAchievable(s: Vector[Int], t: Int) = { // f is (i, j) => Boolean i.e. can the first i elements of s add up to j lazy val f: ((Int, Int)) => Boolean = memoize { case (_, 0) => true // 0 can always be achieved using empty list case (0, _) => false // we can never achieve non-zero if we have empty list case (i, j) => val k = i - 1 // try the kth element f(k, j - s(k)) || f(k, j) } f(s.length, t) }
편집 :로, 여기 아래에 설명 스레드 안전 버전입니다
def memoize[I, O](f: I => O): I => O = new mutable.HashMap[I, O]() {self => override def apply(key: I) = self.synchronized(getOrElseUpdate(key, f(key))) }
-
==============================
2.클래스 / 방법 및 개인 변수의 조합에 특성 수준 발 컴파일. 따라서 재귀 정의가 허용됩니다.
클래스 / 방법 및 개인 변수의 조합에 특성 수준 발 컴파일. 따라서 재귀 정의가 허용됩니다.
반면에 지역 발스는 일반 변수, 따라서 재귀 적 정의는 허용되지 않습니다.
그런데, 데프 당신이 일을 정의 된 경우에도, 당신이 무엇을 기대하지 않을 것입니다. foo는 모든 호출에 새로운 함수 객체의 FIB가 만들어지고 그것의 자신있는 맵이있을 것이다. (당신은 정말 데프가 공개 인터페이스로 원하는 경우) 당신이 대신 일을해야하기 때문이다 :
private val fib: Memo[Int, BigInt] = Memo { case 0 => 0 case 1 => 1 case n => fib(n-1) + fib(n-2) } def foo(n: Int) = { fib(n) }
-
==============================
3.Scalaz가에 대한 해결책을 가지고, 왜 재사용하지?
Scalaz가에 대한 해결책을 가지고, 왜 재사용하지?
import scalaz.Memo lazy val fib: Int => BigInt = Memo.mutableHashMapMemo { case 0 => 0 case 1 => 1 case n => fib(n-2) + fib(n-1) }
당신은 Scalaz에 메모이 제이션에 대한 자세한 내용을보실 수 있습니다.
-
==============================
4.변경 가능한 HashMap의는 스레드로부터 안전하지 않습니다. 또한 기본 조건을 별도로 경우 문을 정의하는 것은 오히려지도가 초기 값으로로드 Memoizer에 전달 될 수 있고, 불필요한 특수 처리가 보인다. 이 메모 (불변지도) 및 공식을 받아 재귀 함수를 반환 Memoizer의 서명이 될 것입니다 다음.
변경 가능한 HashMap의는 스레드로부터 안전하지 않습니다. 또한 기본 조건을 별도로 경우 문을 정의하는 것은 오히려지도가 초기 값으로로드 Memoizer에 전달 될 수 있고, 불필요한 특수 처리가 보인다. 이 메모 (불변지도) 및 공식을 받아 재귀 함수를 반환 Memoizer의 서명이 될 것입니다 다음.
Memoizer과 같을 것이다
def memoize[I,O](memo: Map[I, O], formula: (I => O, I) => O): I => O
이제 다음 피보나치 공식을 주어,
def fib(f: Int => Int, n: Int) = f(n-1) + f(n-2)
Memoizer 피보나치와 같이 정의 될 수있다
val fibonacci = memoize( Map(0 -> 0, 1 -> 1), fib)
여기서 문맥 무관 범용 Memoizer 같이 정의된다
def memoize[I, O](map: Map[I, O], formula: (I => O, I) => O): I => O = { var memo = map def recur(n: I): O = { if( memo contains n) { memo(n) } else { val result = formula(recur, n) memo += (n -> result) result } } recur }
유사 요인에 대한 수식은
def fac(f: Int => Int, n: Int): Int = n * f(n-1)
및 Memoizer와 요인이다
val factorial = memoize( Map(0 -> 1, 1 -> 1), fac)
영감 : 메모이 제이션, 더글러스 크록 포드에 의해 자바 스크립트 좋은 부품의 제 4 장
from https://stackoverflow.com/questions/16257378/is-there-a-generic-way-to-memoize-in-scala by cc-by-sa and MIT license
'SCALA' 카테고리의 다른 글
[SCALA] 스칼라에서 "보기"기능은 무엇입니까? (0) | 2019.11.11 |
---|---|
[SCALA] SBT 정지 종료하지 않고 실행 (0) | 2019.11.11 |
[SCALA] 어떻게를 java.util.List에 scala.List를 변환하는? (0) | 2019.11.11 |
[SCALA] 스파크 dataframe로부터 NULL 값을 필터링하는 방법 (0) | 2019.11.11 |
[SCALA] 어떻게 스파크 SQL에서 내림차순으로 열을 기준으로 정렬하려면? (0) | 2019.11.11 |