2012-06-14 32 views
11

Tôi có một chút tính toán có phần đắt tiền (bắt đầu một cơ sở dữ liệu), và tôi chỉ muốn tạo cơ sở dữ liệu nếu tôi thực sự sẽ sử dụng nó. Tôi đang tìm một biến tham chiếu (hoặc chỉ là một biến đơn giản, nếu có thể) sẽ chỉ đánh giá giá trị của nó trong trường hợp nó được sử dụng (hoặc không tham chiếu). Một cái gì đó khái niệm như sau.Các biến lười biếng có tồn tại trong Clojure không?

(def v (lazy-var (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true)))) 

và trong tương lai, khi tôi hoặc là chỉ cần sử dụng var v, hoặc gọi @v, Sau đó tôi nhận được nó để in ra "CHỨC NĂNG thực sự tốn kém", và từ bản v có giá trị true. Điều quan trọng ở đây là fn không được đánh giá cho đến khi biến được (de) tham chiếu. Khi cần thiết, hàm được đánh giá một lần và chỉ một lần để tính giá trị của biến. Điều này có thể xảy ra trong clojure không?

Trả lời

25

delay sẽ được hoàn hảo cho ứng dụng này:

delay- (delay & body)

Mất một cơ thể của biểu thức và mang lại một đối tượng trễ sẽ gọi cơ thể mới chỉ là lần đầu tiên nó được buộc (với force hoặc deref/@) và sẽ lưu vào bộ nhớ cache kết quả và trả về tất cả các cuộc gọi force sau đó.

Đặt mã để tạo bộ xử lý cơ sở dữ liệu trong nội dung lời gọi delay được lưu trữ dưới dạng biến. Sau đó, dereference Var này bất cứ khi nào bạn cần phải sử dụng DB xử lý - trên dereference đầu tiên cơ thể sẽ được chạy, và trên dereferences tiếp theo xử lý được lưu trữ sẽ được trả lại.

(def db (delay (println "DB stuff") x)) 

(select @db ...) ; "DB stuff" printed, x returned 
(insert @db ...) ; x returned (cached) 
+1

geez, tại sao tôi không nghĩ đến từ đó khi cố gắng tìm kiếm điều này? –

6

Clojure 1.3 giới thiệu chức năng memoize cho mục đích này:

(memoize f)

Trả về một phiên bản memoized của một hàm referentially minh bạch. Phiên bản ghi nhớ của hàm giữ bộ nhớ cache ánh xạ từ các đối số đến kết quả và khi các cuộc gọi có cùng đối số là lặp lại thường xuyên, có hiệu suất cao hơn khi sử dụng bộ nhớ cao hơn sử dụng .

Trong ví dụ của bạn thay thế không tồn tại lười biếng-var với memoize:

(def v (memoize (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true)))) 
(v) 
=>REALLY EXPENSIVE FUNCTION 
=>true 
(v) 
=>true 

(chậm trễ expr) cũng không được công việc như câu trả lời khác giải thích. Một nhận xét thêm về dereferencing sự chậm trễ - sự khác biệt giữa lực lượng và deref/@ là lực lượng không ném ngoại lệ nếu được sử dụng trên biến không chậm trễ trong khi deref/@ có thể ném ClassCastException "không thể được cast vào clojure.lang.IDeref".

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