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を計算します。この場合、メモ化されているため、1まで10の階乗を再度呼び出すことはできません。

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フォームは名前を順番にバインドするため、2番目の関数定義では、参照しようとしたときに名前がl存在しません。(いくつかのマイナーなmodを使用letfnして)使用するか、定義された関数に名前を付けて、代わりに次のように参照することができます。

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

お気づきかもしれませんが、私はリターンをからそれ自体に変更letlますleven。これ(l x y)は、関数に対してのみローカルであり、にアクセスできないバインディングを参照しているため、問題がありましたlet

于 2012-10-18T13:02:00.007 に答える