7

我对 def 和 let 如何以不同方式绑定变量感到困惑。有人可以向我解释为什么这样做:

(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)))))))

但是当我尝试将函数声明为 let 时,它无法编译:

(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)))
4

2 回答 2

8

下面是一个这样的例子来做你所要求的。为了简单起见,我使用阶乘并在阶乘中添加了 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
于 2012-10-18T12:53:13.010 回答
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))

正如您可能注意到的那样,我将返回从 t​​o 更改letl本身,因为这是您想要leven绑定的。是有问题的(l x y),因为它只引用了函数本地的绑定,而let.

于 2012-10-18T13:02:00.007 回答