/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

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