12

Tôi đang cố gắng làm việc qua cuốn sách Lập trình Clojure của Stuart Halloway. Toàn bộ công cụ chức năng này rất mới mẻ đối với tôi.Sự khác biệt giữa hàm Clojure (nth [coll index]) và thành phần (last (take index coll))

Tôi hiểu thế nào

(defn fibo[] 
    (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))) 

tạo dãy Fibonacci uể oải. Tôi không hiểu tại sao

(last (take 1000000 (fibo))) 

công trình, trong khi

(nth (fibo) 1000000) 

ném một OutOfMemoryError. Ai đó có thể giải thích hai biểu thức này khác nhau như thế nào? Là (nth) bằng cách nào đó giữ cho người đứng đầu của trình tự?

Cảm ơn!

+1

Cả hai đều không làm việc với tôi trên tryclj.com vì số lượng quá lớn gây ra tràn. AFAICT bạn không giữ một tham chiếu đến bất cứ điều gì vì vậy tôi không tin bất cứ điều gì là "giữ lên đầu". Bạn có chắc nó không chỉ vì số lượng lớn đến mức không thể tưởng tượng nổi? –

+0

Việc thực hiện cuối cùng là một tiến thẳng tiến O (n) thực hiện đệ quy đuôi, và nó không giữ bất cứ điều gì. nth được thực hiện trong Java và tôi khá chắc chắn rằng nó không giữ bất cứ thứ gì. Do đó, cả hai trình tự của bạn sẽ hoạt động tốt (theo lý thuyết). Điều duy nhất tôi có thể nghĩ đến, mặc dù tôi không rõ ràng về điều này ảnh hưởng đến kết quả, là cuộc gọi thứ n của bạn thực sự tính toán 1 mục nhiều hơn cuộc gọi cuối cùng của bạn. nth 1000000 = 1000001st mục (0 đánh chỉ mục) – vedang

+0

@vedang Cảm ơn ... Tôi sẽ không bắt được sự khác biệt quan trọng đó. Nó không phải là nguồn gốc của vấn đề của tôi, mặc dù tôi đã không nhận ra rằng đối số cần thực hiện là kích thước của chuỗi, trong khi đối số với thứ n là chỉ mục. – Josh

Trả lời

4

Tôi nghĩ bạn đang nói về vấn đề đã được thảo luận trong google group và Rich Hickey cung cấp patch giải quyết được sự cố. Và cuốn sách, tiếng roi được xuất bản sau, không đề cập đến chủ đề này.

Trong clojure 1.3 ví dụ nth của bạn hoạt động với những cải tiến nhỏ trong chức năng fibo. Bây giờ, do những thay đổi trong 1.3, chúng tôi nên gắn cờ M để sử dụng độ chính xác tùy ý hoặc rơi xuống với throwIntOverflow.

(defn fibo[] 
    (map first (iterate (fn [[a b]] [b (+ a b)]) [0M 1M]))) 

Và với những thay đổi này

(nth (fibo) 1000000) 

thành công (nếu bạn có đủ bộ nhớ)

+0

Tôi đã sử dụng phiên bản ảnh chụp được phân phối với sách, 1.1.0-alpha-SNAPSHOT. Thay đổi thành 1.3.0 đã hoạt động. Tôi đoán phiên bản tôi đã chứa lỗi mà bạn đề cập đến ... cụ thể là "Một nỗ lực tối ưu hóa gần đây đã giới thiệu một hop giữ lại đầu trong RT.nth" – Josh

+0

Mặc dù "M" không được yêu cầu. Clojure dường như đang chuyển đổi sang BigInt khi cần thiết, ít nhất là với 1.3.0. – Josh

+0

@Josh, không phải trên máy của tôi. Xem câu trả lời của tôi. –

1

Bạn đang sử dụng phiên bản Clojure nào? Hãy thử (phiên bản clojure) trên repl. Tôi nhận được kết quả giống hệt nhau cho cả hai biểu thức trong 1.3.0, cụ thể là một tràn số nguyên.

Đối

(defn fibo[] 
    (map first (iterate (fn [[a b]] [b (+ a b)]) [(bigint 0) 1]))) 

tôi nhận được kết quả chính xác cho cả hai biểu thức (một số nguyên thực sự lớn ...).

0

Tôi nghĩ rằng bạn có thể đạt đến giới hạn bộ nhớ cụ thể đối với máy tính của bạn, và không phải là một sự khác biệt thực trong hàm.

Nhìn vào mã nguồn cho nth trong https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java nó không giống như một phần n hoặc lấy được giữ đầu.

Tuy nhiên, thứ n sử dụng lập chỉ mục dựa trên không, thay vì đếm theo số mục. Mã của bạn với thứ n chọn phần tử 1000001st của chuỗi (số một tại chỉ số 1000000). Bạn mã với take là trả về phần tử cuối cùng trong một chuỗi yếu tố 1000000. Đó là vật phẩm có chỉ số 999999. Với tốc độ phát triển nhanh của sợi, vật phẩm cuối cùng có thể là thứ đã phá vỡ lưng lạc đà.

Ngoài ra, tôi đã kiểm tra nguồn 1.3.0. Có lẽ các phiên bản trước đó có các triển khai khác nhau. Để fibo của bạn hoạt động bình thường trong 1.3.0, bạn cần phải sử dụng các hàm số học để quảng cáo số cho các hiệu ứng:

(defn fibo[] 
    (map first (iterate (fn [[a b]] [b (+' a b)]) [0 1]))) 
Các vấn đề liên quan