Levenshtein在Clojure中实现了memoization

时间:2017-12-07 15:38:38

标签: clojure memoization levenshtein-distance

这是使用Clojure进行递归的最小Levenshtein(编辑距离)实现:

(defn levenshtein [s1, i1, s2, i2]
  (cond
    (= 0 i1) i2
    (= 0 i2) i1
    :else
    (min (+ (levenshtein s1 (- i1 1) s2 i2) 1)
         (+ (levenshtein s1 i1 s2 (- i2 1)) 1)
         (+ (levenshtein s1 (- i1 1) s2 (- i2 1)) (if (= (subs s1 i1 (+ i1 1)) (subs s2 i2 (+ i2 1))) 0 1))
         )
    )
  )

(defn levenshteinSimple [s1, s2]
  (levenshtein s1, (- (count s1) 1), s2, (- (count s2) 1)))

可以这样使用:

(println (levenshteinSimple "hello", "hilloo"))
(println (levenshteinSimple "hello", "hilloo"))
(println (levenshteinSimple "bananas", "bananas"))
(println (levenshteinSimple "ananas", "bananas"))

打印出来:

2
2
0
1

如何在此实现中添加memoize以提高性能?

请注意:我是Clojure的初学者。这些是我在Clojure中的第一行

1 个答案:

答案 0 :(得分:1)

最简单的方法是使用memoize函数。它需要一个函数,并返回一个memoized函数:

(let [mem-lev (memoize levenshteinSimple]
  (println (mem-lev "hello", "hilloo"))
  (println (mem-lev "hello", "hilloo"))
  (println (mem-lev "bananas", "bananas"))
  (println (mem-lev "ananas", "bananas")))

mem-lev会记住你给它的每个参数以及你的函数返回的结果,如果它已经看到你给它的参数,它将返回缓存的结果。

请注意,这将 not 导致递归调用变为memoized,但任何递归调用都不太可能从memoization中受益。

这也导致您的原始功能被记忆。在此示例中,只会记住mem-lev。如果确实想要记住您的全局函数,您可以将定义更改为:

(def levenshteinSimple
  (memoize
    (fn [s1, s2]
      ...

但我不建议这样做。这导致函数本身保持状态,这不是理想的。它还会在程序长度上保持该状态,如果被滥用,可能会导致内存问题。

(作为一个很好的练习,尝试编写自己的memoize版本。通过这样做我学到了很多东西)。