/core/src/main/scala/scalaz/Memo.scala

http://github.com/scalaz/scalaz · Scala · 98 lines · 62 code · 18 blank · 18 comment · 17 complexity · 8e5dcc7fa19993863463cbf0d91a5f5a MD5 · raw file

  1. package scalaz
  2. import reflect.ClassTag
  3. /** A function memoization strategy. See companion for various
  4. * instances employing various strategies.
  5. */
  6. sealed abstract class Memo[@specialized(Int) K, @specialized(Int, Long, Double) V] {
  7. def apply(z: K => V): K => V
  8. }
  9. sealed abstract class MemoInstances {
  10. }
  11. /** @define immuMapNote As this memo uses a single var, it's
  12. * thread-safe. */
  13. object Memo extends MemoInstances {
  14. def memo[@specialized(Int) K, @specialized(Int, Long, Double) V](f: (K => V) => K => V): Memo[K, V] = new Memo[K, V] {
  15. def apply(z: K => V) = f(z)
  16. }
  17. def nilMemo[@specialized(Int) K, @specialized(Int, Long, Double) V]: Memo[K, V] = memo[K, V](z => z)
  18. private class ArrayMemo[V >: Null : ClassTag](n: Int) extends Memo[Int, V] {
  19. override def apply(f: (Int) => V) = {
  20. val a = Need(new Array[V](n))
  21. k => if (k < 0 || k >= n) f(k) else {
  22. val t = a.value(k)
  23. if (t == null) {
  24. val v = f(k)
  25. a.value(k) = v
  26. v
  27. } else t
  28. }
  29. }
  30. }
  31. private class DoubleArrayMemo(n: Int, sentinel: Double) extends Memo[Int, Double] {
  32. override def apply(f: (Int) => Double) = {
  33. val a = Need {
  34. if (sentinel == 0d) {
  35. new Array[Double](n)
  36. } else {
  37. Array.fill(n)(sentinel)
  38. }
  39. }
  40. k => if (k < 0 || k >= n) f(k) else {
  41. val t = a.value(k)
  42. if (t == sentinel || (sentinel.isNaN && t.isNaN)) {
  43. val v = f(k)
  44. a.value(k) = v
  45. v
  46. } else t
  47. }
  48. }
  49. }
  50. /** Cache results in an `n`-long array. */
  51. def arrayMemo[V >: Null : ClassTag](n: Int): Memo[Int, V] = new ArrayMemo(n)
  52. /** As with `arrayMemo`, but memoizing double results !=
  53. * `sentinel`.
  54. */
  55. def doubleArrayMemo(n: Int, sentinel: Double = 0d): Memo[Int, Double] = new DoubleArrayMemo(n, sentinel)
  56. import scala.collection.mutable
  57. private def mutableMapMemo[K, V](a: mutable.Map[K, V]): Memo[K, V] = memo[K, V](f => k => a.getOrElseUpdate(k, f(k)))
  58. /** Cache results in a [[scala.collection.mutable.HashMap]].
  59. * Nonsensical if `K` lacks a meaningful `hashCode` and
  60. * `java.lang.Object.equals`.
  61. */
  62. def mutableHashMapMemo[K, V]: Memo[K, V] = mutableMapMemo(new mutable.HashMap[K, V])
  63. import scala.collection.immutable
  64. def immutableMapMemo[K, V](m: immutable.Map[K, V]): Memo[K, V] = {
  65. var a = m
  66. memo[K, V](f =>
  67. k => a.getOrElse(k, {
  68. val v = f(k)
  69. a = a updated(k, v)
  70. v
  71. }))
  72. }
  73. /** Cache results in a hash map. Nonsensical unless `K` has
  74. * a meaningful `hashCode` and `java.lang.Object.equals`.
  75. * $immuMapNote
  76. */
  77. def immutableHashMapMemo[K, V]: Memo[K, V] = immutableMapMemo(immutable.HashMap.empty[K, V])
  78. /** Cache results in a tree map. $immuMapNote */
  79. def immutableTreeMapMemo[K: scala.Ordering, V]: Memo[K, V] = immutableMapMemo(new immutable.TreeMap[K, V])
  80. }