2009-07-22 34 views
6

Tôi đã viết 3 hàm đếm số lần một phần tử xuất hiện trong danh sách. Tôi đã thử nhiều đầu vào khác nhau và lược tả nó nhưng tôi vẫn không biết chức năng nào là tốt nhất về hiệu quả sử dụng và hiệu quả thời gian của ngăn xếp. Hãy giúp tôi ra.Chức năng nào là tốt nhất về hiệu quả sử dụng ngăn xếp và thời gian

;; Using an accumulator 
    (defn count-instances1 [a-list an-element] 
     (letfn [(count-aux [list-aux acc] 
         (cond 
          (empty? list-aux) acc 
          :else (if (= (first list-aux) an-element) 
            (count-aux (rest list-aux) (inc acc)) 
            (count-aux (rest list-aux) acc))))] 
     (count-aux a-list 0))) 

;; Normal counting 
    (defn count-instances2 [a-list an-element] 
    (cond 
     (empty? a-list) 0 
     :else 
      (if (= (first a-list) an-element) 
       (+ 1 (count-instances2 (rest a-list) an-element)) 
       (count-instances2 (rest a-list) an-element)))) 

;; using loop. does this help at all? 
    (defn count-instances3 [a-list an-element] 
     (loop [mylist a-list acount 0] 
      (if (empty? mylist) 
       acount 
       (if (= (first mylist) an-element) 
       (recur (rest mylist)(inc acount)) 
       (recur (rest mylist) acount))))) 
+3

Kết quả của những nỗ lực lược tả của bạn là gì? –

+3

Nested 'defn' có lẽ không làm những gì bạn nghĩ. 'defn' luôn định nghĩa một hàm toplevel. Bạn có thể sử dụng 'letfn' (hoặc thậm chí' (cho phép [f (fn ...)]) ') nếu bạn muốn định nghĩa một hàm bên trong. –

+0

Cảm ơn Brian. Nhưng tôi không thể nhận được letfn để làm việc. Bạn có thể chỉnh sửa câu hỏi của tôi với letfn không? Cảm ơn rất nhiều. – unj2

Trả lời

2

Phiên bản loop/tái phát là đúng cách. Clojure không thể tối ưu hóa các cuộc gọi đuôi do các hạn chế của JVM.

+3

Điều đó không chính xác. Bạn nên nói rằng Clojure * chọn * không để tối ưu hóa các cuộc gọi đuôi, bởi vì đó là khá khó khăn để đạt được trong một ngôn ngữ (Java) mà không có chúng. Trên thực tế, có một số cách triển khai ngôn ngữ (ví dụ: SISC - http://sisc-scheme.org/) trên đầu trang của JVM để tối ưu hóa các cuộc gọi đuôi. –

+0

Thats thực sự lạ. Tại sao nó không muốn tối ưu hóa các cuộc gọi đuôi? Nó có thể đã cứu chúng ta rất nhiều phức tạp? Có thương mại không? – unj2

+3

'recur' là tốt đẹp bởi vì nó rõ ràng và bạn chỉ có thể sử dụng nó từ vị trí đuôi (trình biên dịch phàn nàn nếu không) mà bắt trường hợp mà bạn nghĩ rằng bạn đang tail-recursing nhưng bạn không. Nó có thể tất cả được macro'd đi tự động, nhưng nó không phải là nhiều rắc rối để thay thế tên chức năng của bạn trong cuộc gọi đuôi với biểu tượng 'recur'. –

0

Viết mã của bạn để trình biên dịch/interperter có thể optmize nó cho đệ quy đuôi, sẽ dẫn đến tăng hiệu suất và giảm mức sử dụng ngăn xếp. Tôi nghĩ rằng chức năng đếm bình thường của bạn có thể đủ điều kiện cho đệ quy đuôi để một trong những nên được khá nhanh. Không chắc lắm vì tôi chỉ tranh giành Lisp như một sở thích.

http://en.wikipedia.org/wiki/Tail_recursion

+0

Clojure không thể tối ưu hóa các cuộc gọi đuôi do các hạn chế của JVM. – Svante

+1

Nhận xét của Rich Hickey về điều này là tốt hơn nên có một sự abstaction rõ ràng hoạt động mọi lúc (recur) hơn một cái ngầm ẩn chứa hầu hết thời gian (do phức tạp của việc này trên JVM) –

+0

@Svante - Clojure có thể và tối ưu hóa các cuộc gọi đuôi. Bạn chỉ cần rõ ràng rằng bạn muốn nó bằng cách sử dụng 'recur' (đó là một lựa chọn thiết kế ngôn ngữ của Rich Hickey). Hoàn toàn có thể làm TCO trên JVM hầu hết thời gian. Các giới hạn JVM chỉ ảnh hưởng đến sự đồng quy ước chung (đó thực sự là một trường hợp khá hiếm). – mikera

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