2012-10-18 55 views
7

我很困惑,如何def和让变量绑定不同。谁能给我解释一下为什么这个工程:让函数递归

(def leven 
    (memoize (fn [x y] 
    (cond (empty? x) (count y) 
     (empty? y) (count x) 
     :else (min (+ (leven (rest x) y) 1) 
        (+ (leven x (rest y)) 1) 
        (+ (leven (rest x) (rest y)) (if (= (first x) (first y)) 0 1)) 
      ) 
))) 
) 

但是当我尝试申报功能让它无法编译:

(def leven 
    (let [l (memoize (fn [x y] 
    (cond (empty? x) (count y) 
      (empty? y) (count x) 
      :else (min (+ (l (rest x) y) 1) 
         (+ (l x (rest y)) 1) 
         (+ (l (rest x) (rest y)) (if (= (first x) (first y)) 0 1)) 
       ) 
    ) 
    ))] 
    (l x y) 
    ) 
) 

编辑:这工作,使用显示由ANKUR技术。

(defn leven [x y] 
(let [l (memoize (fn [f x y] 
(cond (empty? x) (count y) 
     (empty? y) (count x) 
     :else (min (+ (f f (rest x) y) 1) 
        (+ (f f x (rest y)) 1) 
        (+ (f f (rest x) (rest y)) (if (= (first x) (first y)) 0 1)) 
      ) 
) 
)) 
magic (partial l l)] 
(magic x y) 
) 
) 

回答

7

下面是一个这样的例子来做你所要求的。我使用的阶乘只是为了简单起见和阶乘加入调用println以确保记忆化工作正常

(let [fact (memoize (fn [f x] 
         (println (str "Called for " x)) 
         (if (<= x 1) 1 (* x (f f (- x 1)))))) 
     magic (partial fact fact)] 
    (magic 10) 
    (magic 11)) 

首先计算的10阶乘,然后11在这种情况下,不应该再打电话因子为10至1已被记忆。

Called for 10 
Called for 9 
Called for 8 
Called for 7 
Called for 6 
Called for 5 
Called for 4 
Called for 3 
Called for 2 
Called for 1 
Called for 11 
39916800 
+0

非常有趣。所以你基本上只是传递函数作为参数,所以编译器不会对它没有被定义的困惑。我现在不能尝试这个,但我稍后会尝试这种方法。 – onit

6

let形式结合的名称依次所以在你的第二个函数定义,当您尝试引用它的名字l不存在。您可以使用letfn(有一些小的MODS)或给予定义函数的名称,而是指的是代替,像这样:

(def leven 
    (let [l (memoize (fn SOME-NAME [x y] 
    (cond 
     (empty? x) (count y) 
     (empty? y) (count x) 
     :else (min (+ (SOME-NAME (rest x) y) 1) 
       (+ (SOME-NAME x (rest y)) 1) 
       (+ (SOME-NAME (rest x) (rest y)) (if (= (first x) (first y)) 0 1))))))] 
l)) 

正如你可能会注意到我改变来自let回报是l本身因为那是你想要的leven(l x y)是有问题的,因为它只涉及功能本地绑定,并且不可访问let

+2

功能SOME-NAME在使用时会失去memoize的好处吗?你不需要调用函数memoize returns,还是不可能在let语句中使用递归memoized函数? – onit

+1

@onit通过移动'SOME-NAME'作为第一个参数:'(fn [SOME-NAME xy]',然后还可以将调用替换为'SOME'来修改定义'leven'以获得记忆的好处-NAME'改为'(SOME-NAME SAME-NAME ...)',最后将返回值'l'替换为'(partial ll)'。 –