PageRenderTime 45ms CodeModel.GetById 19ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/core/src/main/scala/scalaz/MetricSpace.scala

http://github.com/scalaz/scalaz
Scala | 94 lines | 57 code | 19 blank | 18 comment | 14 complexity | 29c4c56b109db3b5a8d1e15a734ca2f2 MD5 | raw file
 1package scalaz
 2
 3////
 4/**
 5 * Useful metric spaces include the manhattan distance between two points,
 6 * the Levenshtein edit distance between two strings, the number of
 7 * edges in the shortest path between two nodes in an undirected graph
 8 * and the Hamming distance between two binary strings. Any euclidean
 9 * space also has a metric. However, in this module we use int-valued
10 * metrics and that's not compatible with the metrics of euclidean
11 * spaces which are real-values.
12 *
13 * @see [[scalaz.BKTree]]
14 */
15////
16@deprecated("Redundant to spire's `MetricSpace`", "7.0.1")
17trait MetricSpace[F]  { self =>
18  ////
19  def distance(a: F, b: F): Int
20
21  def contramap[B](f: B => F): MetricSpace[B] = new MetricSpace[B] {
22    def distance(a: B, b: B): Int = self.distance(f(a), f(b))
23  }
24
25  // derived functions
26
27  trait MetricSpaceLaw {
28
29    import std.boolean.conditional
30
31    def nonNegativity(a1: F, a2: F): Boolean = distance(a1, a1) >= 0
32    def identity(a1: F): Boolean = distance(a1, a1) == 0
33    def equality(a1: F, a2: F)(implicit F: Equal[F]): Boolean = conditional(F.equal(a1, a2), distance(a1, a2) == 0)
34    def symmetry(a1: F, a2: F): Boolean = distance(a1, a2) == distance(a2, a1)
35    def triangleInequality(a1: F, a2: F, a3: F): Boolean = (distance(a1, a2) + distance(a2, a3)) >= distance(a1, a3)
36  }
37
38  def metricSpaceLaw = new MetricSpaceLaw {}
39
40  ////
41  val metricSpaceSyntax = new scalaz.syntax.MetricSpaceSyntax[F] { def F = MetricSpace.this }
42}
43
44object MetricSpace {
45  @inline def apply[F](implicit F: MetricSpace[F]): MetricSpace[F] = F
46
47  ////
48  val metricSpaceInstance = new Contravariant[MetricSpace] {
49    def contramap[A, B](r: MetricSpace[A])(f: B => A): MetricSpace[B] = r contramap f
50  }
51
52  def metricSpace[A](f: (A, A) => Int): MetricSpace[A] = new MetricSpace[A] {
53    def distance(a1: A, a2: A): Int = f(a1, a2)
54  }
55
56  def levenshtein[F[_], A](implicit l: Length[F], i: Index[F], e: Equal[A]): MetricSpace[F[A]] = new MetricSpace[F[A]] {
57    def distance(a1: F[A], a2: F[A]): Int = levenshteinDistance(a1, a2)
58  }
59
60  def levenshteinDistance[F[_], A](value: F[A], w: F[A])(implicit l: Length[F], ind: Index[F], equ: Equal[A]): Int = {
61    import Memo._
62    def levenshteinMatrix(w: F[A])(implicit l: Length[F], ind: Index[F], equ: Equal[A]): (Int, Int) => Int = {
63      val m = mutableHashMapMemo[(Int, Int), Int]
64
65      def get(i: Int, j: Int): Int = if (i == 0) j
66      else if (j == 0) i
67      else {
68        lazy val t: A = ind.index(value, (i - 1)).get
69        lazy val u: A = ind.index(w, (j - 1)).get
70        lazy val e: Boolean = equ.equal(t, u)
71
72
73        val g: ((Int, Int)) => Int = m {
74          case (a, b) => get(a, b)
75        }
76        val a: Int = g((i - 1, j)) + 1
77        val b: Int = g((i - 1, j - 1)) + (if (e) 0 else 1)
78        def c: Int = g((i, j - 1)) + 1
79        if (a < b) a else if (b <= c) b else c
80      }
81
82      get
83    }
84
85    val k = levenshteinMatrix(w)
86    k(l.length(value), l.length(w))
87  }
88
89  implicit def LevenshteinString: MetricSpace[String] = {
90    import std.list._, std.anyVal._
91    levenshtein[List, Char].contramap((s: String) => s.toList)
92  }
93  ////
94}