gpt4 book ai didi

Scala 内存 : How does this Scala memo work?

转载 作者:行者123 更新时间:2023-12-03 10:49:39 26 4
gpt4 key购买 nike

以下代码来自 Pathikrit's Dynamic Programming存储库。
我对它的美丽和独特性感到困惑。

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

我的问题是:
  • 为什么是 type K声明为 (Int, Int)在子集总和中?
  • int 有什么用在 (Int, Int)分别代表?

  • 3.如何 (List[Int], Int)隐式转换为 (Int, Int) ?
    我看没有 implicit def foo(x:(List[Int],Int)) = (x._1.toInt,x._2) . (甚至在它导入的 Implicits.scala 文件中也不行。

    *编辑:嗯,我想念这个:
    implicit def encode(key: (List[Int], Int)) = (key._1.length, key._2)

    我喜欢 Pathikrit 的图书馆 scalgos非常。里面有很多Scala珍珠。请帮我解决这个问题,这样我才能欣赏 Pathikrit 的智慧。谢谢你。 (:

    最佳答案

    我是 above code 的作者.

    /**
    * Generic way to create memoized functions (even recursive and multiple-arg ones)
    *
    * @param f the function to memoize
    * @tparam I input to f
    * @tparam K the keys we should use in cache instead of I
    * @tparam O output of f
    */
    case class Memo[I <% K, K, O](f: I => O) extends (I => O) {
    import collection.mutable.{Map => Dict}
    type Input = I
    type Key = K
    type Output = O
    val cache = Dict.empty[K, O]
    override def apply(x: I) = cache getOrElseUpdate (x, f(x))
    }

    object Memo {
    /**
    * Type of a simple memoized function e.g. when I = K
    */
    type ==>[I, O] = Memo[I, I, O]
    }

    Memo[I <% K, K, O] :
    I: input
    K: key to lookup in cache
    O: output

    线路 I <% K表示 K可以 viewable (即隐式转换)来自 I .

    大多数情况下, I应该是 K例如如果您正在写信 fibonacci这是 Int => Int 类型的函数,可以通过 Int缓存本身。

    但是,有时在编写备忘录时,您不希望总是通过输入本身( I )来内存或缓存,而是希望根据输入( K )进行内存或缓存,例如在编写 subsetSum 时输入类型为 (List[Int], Int) 的算法,您不想使用 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)
    }

    您当然可以将所有这些缩短为一行: type DP = Memo[(List[Int], Int), (Int, Int), Boolean]
    对于常见情况(当 I = K 时),您可以简单地执行以下操作: type ==>[I, O] = Memo[I, I, O]并像这样使用它到 calculate the binomial coeff递归内存:
      /**
    * 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)
    }

    要查看上述语法如何工作的详细信息,请refer to this question .

    这是一个计算 editDistance 的完整示例通过对输入 (Seq, Seq) 的两个参数进行编码至 (Seq.length, Seq.length) :
     /**
    * 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))

    关于Scala 内存 : How does this Scala memo work?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25129721/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com