/core/src/main/scala/scalaz/MetricSpace.scala
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}