una tiempo relajante para música musica mucho memorizar memoria mejor ley examen estudiar escuela como scala scope dynamic-programming memoization forward-reference

scala - tiempo - música para estudiar y memorizar



¿Hay una manera genérica de memorizar en Scala? (4)

Class / rasgo nivel val compila a una combinación de un método y una variable privada. Por lo tanto, se permite una definición recursiva.

Los val locales, por otro lado, son solo variables regulares, por lo que no se permite la definición recursiva.

Por cierto, incluso si la def que definió funcionó, no haría lo que esperaba. En cada invocación de foo se creará un nuevo objeto de función fib y tendrá su propio mapa de respaldo. Lo que debería hacer en su lugar es esto (si realmente quiere que una def su interfaz pública):

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

Quería memorizar esto:

def fib(n: Int) = if(n <= 1) 1 else fib(n-1) + fib(n-2) println(fib(100)) // times out

Así que escribí esto y esto sorprendentemente compila y funciona (estoy sorprendido porque fib referencia en su declaración):

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

Pero cuando trato de declarar fib dentro de una def , obtengo un error de compilación:

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

Arriba no se puede compilar el error: forward reference extends over definition of value fib case n => fib(n-1) + fib(n-2)

¿Por qué falla la declaración de val fib dentro de una definición, pero falla en el alcance de clase / objeto?

Para aclarar, ¿por qué podría querer declarar la función memorizada recursiva en el ámbito def? Aquí está mi solución al problema de suma de subconjuntos:

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


Encontré una mejor manera de memorizar usando Scala:

def memoize[I, O](f: I => O): I => O = new mutable.HashMap[I, O]() { override def apply(key: I) = getOrElseUpdate(key, f(key)) }

Ahora puede escribir fibonacci de la siguiente manera:

lazy val fib: Int => BigInt = memoize { case 0 => 0 case 1 => 1 case n => fib(n-1) + fib(n-2) }

Aquí hay uno con múltiples argumentos (la función de elegir):

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

Y aquí está el problema de la suma de subconjuntos:

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

EDITAR: Como se explica a continuación, aquí hay una versión segura para subprocesos

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


Mutable HashMap no es seguro para subprocesos. También la definición de las declaraciones de casos por separado para las condiciones base parece un manejo especial innecesario, en lugar de eso, Map puede cargarse con los valores iniciales y pasarse a Memoizer. Lo siguiente sería la firma de Memoizer donde acepta una nota (Mapa inmutable) y fórmula y devuelve una función recursiva.

Memoizer se vería como

def memoize[I,O](memo: Map[I, O], formula: (I => O, I) => O): I => O

Ahora con la siguiente fórmula de Fibonacci,

def fib(f: Int => Int, n: Int) = f(n-1) + f(n-2)

fibonacci con Memoizer se puede definir como

val fibonacci = memoize( Map(0 -> 0, 1 -> 1), fib)

donde el memorándum de propósito general agnóstico de contexto se define como

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 }

Del mismo modo, para factorial, una fórmula es

def fac(f: Int => Int, n: Int): Int = n * f(n-1)

y factorial con Memoizer es

val factorial = memoize( Map(0 -> 1, 1 -> 1), fac)

Inspiration: Memoization, Capítulo 4 de las partes buenas de Javascript por Douglas Crockford


Scalaz tiene una solución para eso, ¿por qué no reutilizarla?

import scalaz.Memo lazy val fib: Int => BigInt = Memo.mutableHashMapMemo { case 0 => 0 case 1 => 1 case n => fib(n-2) + fib(n-1) }

Puede leer más sobre la memorización en Scalaz .