在Scala中用于存储内存中可变数据表的类型是什么?

Iva*_*van 20 scala memoization data-structures scala-collections

每次调用一个函数时,如果给定的一组参数值的结果尚未被记忆,我想将结果放入内存表中.一列用于存储结果,另一列用于存储参数值.

我该如何最好地实现这一点?争论的种类繁多,包括一些枚举.

在C#中,我通常使用DataTable.Scala中有同等的东西吗?

Aar*_*rup 25

你可以使用a mutable.Map[TupleN[A1, A2, ..., AN], R] ,或者如果内存是一个问题,WeakHashMap [1].下面的定义(基于michid博客的memoization代码)允许您轻松地使用多个参数记忆函数.例如:

import Memoize._

def reallySlowFn(i: Int, s: String): Int = {
   Thread.sleep(3000)
   i + s.length
}

val memoizedSlowFn = memoize(reallySlowFn _)
memoizedSlowFn(1, "abc") // returns 4 after about 3 seconds
memoizedSlowFn(1, "abc") // returns 4 almost instantly
Run Code Online (Sandbox Code Playgroud)

定义:

/**
 * A memoized unary function.
 *
 * @param f A unary function to memoize
 * @param [T] the argument type
 * @param [R] the return type
 */
class Memoize1[-T, +R](f: T => R) extends (T => R) {
   import scala.collection.mutable
   // map that stores (argument, result) pairs
   private[this] val vals = mutable.Map.empty[T, R]

   // Given an argument x, 
   //   If vals contains x return vals(x).
   //   Otherwise, update vals so that vals(x) == f(x) and return f(x).
   def apply(x: T): R = vals getOrElseUpdate (x, f(x))
}

object Memoize {
   /**
    * Memoize a unary (single-argument) function.
    *
    * @param f the unary function to memoize
    */
   def memoize[T, R](f: T => R): (T => R) = new Memoize1(f)

   /**
    * Memoize a binary (two-argument) function.
    * 
    * @param f the binary function to memoize
    * 
    * This works by turning a function that takes two arguments of type
    * T1 and T2 into a function that takes a single argument of type 
    * (T1, T2), memoizing that "tupled" function, then "untupling" the
    * memoized function.
    */
   def memoize[T1, T2, R](f: (T1, T2) => R): ((T1, T2) => R) = 
      Function.untupled(memoize(f.tupled))

   /**
    * Memoize a ternary (three-argument) function.
    *
    * @param f the ternary function to memoize
    */
   def memoize[T1, T2, T3, R](f: (T1, T2, T3) => R): ((T1, T2, T3) => R) =
      Function.untupled(memoize(f.tupled))

   // ... more memoize methods for higher-arity functions ...

   /**
    * Fixed-point combinator (for memoizing recursive functions).
    */
   def Y[T, R](f: (T => R) => T => R): (T => R) = {
      lazy val yf: (T => R) = memoize(f(yf)(_))
      yf
   }
}
Run Code Online (Sandbox Code Playgroud)

定点组合子(Memoize.Y)可以记忆递归函数:

val fib: BigInt => BigInt = {                         
   def fibRec(f: BigInt => BigInt)(n: BigInt): BigInt = {
      if (n == 0) 1 
      else if (n == 1) 1 
      else (f(n-1) + f(n-2))                           
   }                                                     
   Memoize.Y(fibRec)
}
Run Code Online (Sandbox Code Playgroud)

[1] WeakHashMap不能很好地用作缓存.请参阅http://www.codeinstructions.com/2008/09/weakhashmap-is-not-cache-understanding.html以及此相关问题.


Lan*_*dei 10

anovstrup使用可变Map建议的版本与C#中的版本基本相同,因此易于使用.

但如果你想要,你也可以使用更实用的风格.它使用不可变映射,它充当一种控制器.将Tuples(而不是示例中的Int)作为键与在可变的情况下完全一样.

def fib(n:Int) = fibM(n, Map(0->1, 1->1))._1

def fibM(n:Int, m:Map[Int,Int]):(Int,Map[Int,Int]) = m.get(n) match {
   case Some(f) => (f, m)
   case None => val (f_1,m1) = fibM(n-1,m)
                val (f_2,m2) = fibM(n-2,m1)
                val f = f_1+f_2
                (f, m2 + (n -> f))   
}
Run Code Online (Sandbox Code Playgroud)

当然这有点复杂,但是要知道一种有用的技术(注意上面的代码旨在清晰,而不是速度).