Recursion let函数中的递归

Recursion let函数中的递归,recursion,clojure,let,Recursion,Clojure,Let,我对def和let如何以不同方式绑定变量感到困惑。有人能给我解释一下为什么会这样: (def leven (回忆) (fn[x y] (条件(空?x)(计数y) (空?y)(计数x) :else(最小(+(剩余x)y)1) (+(leven x(rest y))1) (+(leven(rest x)(rest y))(如果(=(first x)(first y))0 1()()()())((first x)40 但当我试图将函数声明为let时,它无法编译: (def leven (让[l(me

我对def和let如何以不同方式绑定变量感到困惑。有人能给我解释一下为什么会这样:

(def leven
(回忆)
(fn[x y]
(条件(空?x)(计数y)
(空?y)(计数x)
:else(最小(+(剩余x)y)1)
(+(leven x(rest y))1)
(+(leven(rest x)(rest y))(如果(=(first x)(first y))0 1()()()())((first x)40
但当我试图将函数声明为let时,它无法编译:

(def leven
(让[l(memoize)(fn[x y]
(条件(空?x)(计数y)
(空?y)(计数x)
:else(最小(+(l(剩余x)y)1)
(+(长x(其余y))1)
(+(l(剩余x)(剩余y))(如果(=(第一个x)(第一个y))0 1()()()()))]
(l x y)))
编辑:使用Ankur展示的技术,这项工作很有效

(defn leven[x y]
(让[l(memoize)(fn[f x y]
(条件(空?x)(计数y)
(空?y)(计数x)
:else(最小(+(剩余x)y)1)
(+(fx(剩余y))1)
(+(f(剩余x)(剩余y))(如果(=(第一个x)(第一个y))0 1‘‘‘‘‘‘)
魔术(部分l)]
(魔法x y)))

下面就是这样一个例子,可以满足您的要求。为了简单起见,我使用了factorial,并在factorial中添加了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))

let
表单按顺序绑定名称,因此在第二个函数定义中,当您尝试引用它时,名称
l
不存在。您可以使用
letfn
(与一些次要mod一起使用),或者为定义的函数指定一个名称,并改为引用该名称,如下所示:

(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

函数SOME-NAME在这样使用时是否会失去memoize的好处?您不需要调用函数memoize returns吗,或者在let语句中不可能有递归的memoized函数吗?@onit可以修改定义
leven
,通过移动
SOME-NAME
作为第一个参数来获得memoized的好处:
(fn[SOME-NAME x y]
,然后将对
SOME-NAME
的调用替换为
(SOME-NAME SOME-NAME…
,最后将返回值
l
替换为
(部分l)
。非常有趣。因此,您基本上只是将函数作为参数传入,这样编译器就不会因为没有定义它而感到困惑。我现在无法尝试这种方法,但稍后我将尝试这种方法。
(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))