2012-10-18 43 views
7

Tôi đang bối rối như thế nào def và để cho các biến ràng buộc khác nhau. Ai đó có thể giải thích cho tôi lý do tại sao công trình này:Đệ quy bên trong cho phép chức năng

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

Nhưng khi tôi cố gắng để khai báo các chức năng như để cho nó thất bại trong việc biên dịch:

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

EDIT: Công việc này, sử dụng kỹ thuật cho thấy bởi 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) 
) 
) 

Trả lời

7

Dưới đây là ví dụ để làm những gì bạn đã yêu cầu. Tôi đang sử dụng thừa chỉ vì lợi ích của sự đơn giản và thêm vào println trong giai thừa để đảm bảo memoization đang làm việc tốt

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

Đầu tiên tính toán thừa của 10 và sau đó 11 trong trường hợp này nó không nên lại gọi giai thừa trong 10 đến 1 như đã được ghi nhớ.

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

Rất thú vị. Vì vậy, bạn về cơ bản chỉ cần đi qua trong các chức năng như một đối số để trình biên dịch không bị lẫn lộn về nó không được xác định. Tôi không thể thử điều này ngay bây giờ, nhưng tôi sẽ thử phương pháp này sau. – onit

6

Dạng let liên kết tên tuần tự như vậy trong định nghĩa hàm thứ hai của bạn tên l không tồn tại khi bạn cố gắng đề cập đến nó. Bạn có thể có thể sử dụng letfn (với một số mods nhỏ) hoặc cung cấp cho các chức năng được xác định một tên và thay vào đó tham khảo mà thay vào đó, như vậy:

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

Như bạn có thể thấy tôi thay đổi trở về từ letl bản thân vì đó là những gì bạn muốn leven ràng buộc. (l x y) có vấn đề bởi vì nó chỉ liên kết đến các liên kết chỉ cục bộ với hàm và không thể truy cập vào let.

+2

Chức năng SOME-NAME có mất đi lợi ích của việc ghi nhớ khi sử dụng như vậy không? Bạn không cần phải gọi chức năng ghi nhớ trả về, hoặc là nó không thể có một chức năng ghi nhớ đệ quy trong một tuyên bố let? – onit

+1

@onit Định nghĩa 'leven' có thể được sửa đổi để thu được lợi ích của việc ghi nhớ bằng cách di chuyển' MỘT SỐ TÊN' làm đối số đầu tiên: '(fn [SOME-NAME xy]', và sau đó bằng cách thay thế các cuộc gọi đến 'SOME -NAME' thành '(MỘT SỐ TÊN-TÊN-NAME ...)' và cuối cùng bằng cách thay thế giá trị trả về 'l' thành' (partial ll) '. –

Các vấn đề liên quan