Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala备忘录:这个Scala备忘录是如何工作的?_Scala_Dynamic Programming_Memoization - Fatal编程技术网

Scala备忘录:这个Scala备忘录是如何工作的?

Scala备忘录:这个Scala备忘录是如何工作的?,scala,dynamic-programming,memoization,Scala,Dynamic Programming,Memoization,以下代码来自存储库。 我对它的美丽和独特感到迷惑 def subsetSum(s: List[Int], t: Int) = { type DP = Memo[(List[Int], Int), (Int, Int), Seq[Seq[Int]]] implicit def encode(key: (List[Int], Int)) = (key._1.length, key._2) lazy val f: DP = Memo { case (Nil, 0) => S

以下代码来自存储库。 我对它的美丽和独特感到迷惑

def subsetSum(s: List[Int], t: Int) = {
  type DP = Memo[(List[Int], Int), (Int, Int), Seq[Seq[Int]]]
  implicit def encode(key: (List[Int], Int)) = (key._1.length, key._2)

  lazy val f: DP = Memo {
    case (Nil, 0) => Seq(Nil)
    case (Nil, _) => Nil
    case (a :: as, x) => (f(as, x - a) map {_ :+ a}) ++ f(as, x)
  }

  f(s, t)
}
类型
Memo
在另一个文件中实现:

case class Memo[I <% K, K, O](f: I => O) extends (I => O) {
  import collection.mutable.{Map => Dict}
  val cache = Dict.empty[K, O]
  override def apply(x: I) = cache getOrElseUpdate (x, f(x))
}
我非常喜欢Pathikrit的图书馆。里面有很多斯卡拉珍珠。请帮帮我,这样我才能欣赏帕西克里特的智慧。非常感谢。(:

我是这本书的作者

I Int
,可以通过
Int
本身进行缓存

但是,有时在编写回忆录时,您不希望总是通过输入本身(
I
)进行回忆录或缓存,而是希望使用输入的函数(
K
),例如,当您编写具有类型为
(List[Int],Int)
的输入的
subsetSum
算法时,您不希望使用
List[Int]
作为缓存中的键,但您希望使用
List[Int].size
作为缓存中键的一部分

下面是一个具体的例子:

/**
 * Subset sum algorithm - can we achieve sum t using elements from s?
 * O(s.map(abs).sum * s.length)
 *
 * @param s set of integers
 * @param t target
 * @return true iff there exists a subset of s that sums to t
 */
 def isSubsetSumAchievable(s: List[Int], t: Int): Boolean = {
    type I = (List[Int], Int)     // input type
    type K = (Int, Int)           // cache key i.e. (list.size, int)
    type O = Boolean              // output type      

    type DP = Memo[I, K, O]

    // encode the input as a key in the cache i.e. make K implicitly convertible from I
    implicit def encode(input: DP#Input): DP#Key = (input._1.length, input._2)   

    lazy val f: DP = Memo {
      case (Nil, x) => x == 0      // an empty sequence can only achieve a sum of zero
      case (a :: as, x) => f(as, x - a) || f(as, x)      // try with/without a.head
    }

    f(s, t)
 }
当然,您可以将所有这些内容缩短为一行:
键入DP=Memo[(列表[Int],Int,(Int,Int),Boolean]

对于常见情况(当
I=K
时),您只需执行以下操作:
type=>I,O]=Memo[I,I,O]
并像这样使用它来进行递归记忆:

  /**
   * http://mathworld.wolfram.com/Combination.html
   * @return memoized function to calculate C(n,r)
   */
  val c: (Int, Int) ==> BigInt = Memo {
    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)
  }
要查看上述语法的详细工作原理,请。

下面是一个完整的示例,它通过将输入
(Seq,Seq)
的两个参数编码为
(Seq.length,Seq.length)
来计算:

最后,典型的斐波那契例子:

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

println(fib(100))

关于#3,你忘了读带有
隐式def encode
的行,我已经看到了这个问题,但我仍然想知道value
f
真的会记忆吗?似乎每次调用
subsetSum
时,都会创建一个新的Memo实例,它不是我们想要的最后一个Memo。这是真的吗?是的,对于给定的
s
t
,它返回一个
Memo
对象。如果之前计算过该
Memo
对象,则对该
Memo
对象的进一步调用将是
O(1)
。您可以通过编写一个简单的斐波那契记忆函数来说服自己,例如lazy val factorial:Int=>BigInt=Memo{case 0=>1 case n=>n*factorial(n-1)}另一种说服自己的方法是把这一行放在Memo.scala中:cache getOrElseUpdate(x,(y)=>println(s“cache miss:$y”);f(y))@请看这是测试结果。Memo似乎不起作用……做得很好。只要一件事:
case class
不应该在这里使用,因为
Memo
包含一个具有移动散列的成员。我在哪里可以找到关于
=>
表示法的信息?@Krever:看这行:
type=>[I,O]=Memo[I,I,O]
通常这些东西被称为
中缀类型
。请参阅:为什么要使用lazy,例如在
lazy val f:DP=Memo{…}
?@ipburbank:因为
f
在其自身定义中引用了自身。
/**
 * Subset sum algorithm - can we achieve sum t using elements from s?
 * O(s.map(abs).sum * s.length)
 *
 * @param s set of integers
 * @param t target
 * @return true iff there exists a subset of s that sums to t
 */
 def isSubsetSumAchievable(s: List[Int], t: Int): Boolean = {
    type I = (List[Int], Int)     // input type
    type K = (Int, Int)           // cache key i.e. (list.size, int)
    type O = Boolean              // output type      

    type DP = Memo[I, K, O]

    // encode the input as a key in the cache i.e. make K implicitly convertible from I
    implicit def encode(input: DP#Input): DP#Key = (input._1.length, input._2)   

    lazy val f: DP = Memo {
      case (Nil, x) => x == 0      // an empty sequence can only achieve a sum of zero
      case (a :: as, x) => f(as, x - a) || f(as, x)      // try with/without a.head
    }

    f(s, t)
 }
  /**
   * http://mathworld.wolfram.com/Combination.html
   * @return memoized function to calculate C(n,r)
   */
  val c: (Int, Int) ==> BigInt = Memo {
    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)
  }
 /**
   * Calculate edit distance between 2 sequences
   * O(s1.length * s2.length)
   *
   * @return Minimum cost to convert s1 into s2 using delete, insert and replace operations
   */
  def editDistance[A](s1: Seq[A], s2: Seq[A]) = {

    type DP = Memo[(Seq[A], Seq[A]), (Int, Int), Int]
    implicit def encode(key: DP#Input): DP#Key = (key._1.length, key._2.length)

    lazy val f: DP = Memo {
      case (a, Nil) => a.length
      case (Nil, b) => b.length
      case (a :: as, b :: bs) if a == b => f(as, bs)
      case (a, b) => 1 + (f(a, b.tail) min f(a.tail, b) min f(a.tail, b.tail))
    }

    f(s1, s2)
  }
lazy val fib: Int ==> BigInt = Memo {
  case 0 => 0
  case 1 => 1
  case n if n > 1 => fib(n-1) + fib(n-2)
}

println(fib(100))