2015-04-26 14 views
9

Hãy nói rằng tôi chạy sauphụ thêm vào kết quả của một "loop-thu thập" trong Lisp

(loop for i to 4 collect i) 

Sau đó, tôi nhận được một danh sách (0 1 2 3 4). Bây giờ, nếu tôi muốn nối thêm thứ gì đó vào kết quả, tôi có thể sử dụng rplacd trên phần tử last của nó, nhưng vì danh sách Lisp là các danh sách liên kết, nên nó không hiệu quả lắm. Ở đây danh sách rất nhỏ, nhưng nó chỉ là một ví dụ.

Tuy nhiên, vì cơ sở vòng lặp trả về danh sách theo thứ tự tăng dần, nó phải theo dõi con trỏ đến phần tử cuối cùng và cập nhật kết quả bằng rplacd hoặc tương đương. Một macroexpand-all cho thấy đó là những gì CCL làm, và có lẽ là các lisps khác nữa.

Câu hỏi: Có cách nào để sử dụng "con trỏ" này trong mệnh đề finally không? Nó sẽ cho phép một để thêm một cái gì đó cho kết quả, mà đôi khi hữu ích.

Tất nhiên, thật dễ dàng để mã hóa máy móc con trỏ, nhưng nó không quá đẹp. Ví dụ: thông tin sau đây sẽ thêm danh sách e vào danh sách (0 1 ... n).

(defun foo (n e) 
    (let* ((a (list nil)) (tail a)) 
     (loop for i to n 
       do (rplacd tail (setf tail (list i))) 
       finally (rplacd tail (setf tail e)) 
       (return (cdr a))))) 

Trả lời

6

Một so sánh bổ sung cho mỗi lần lặp và một lần lặp thêm cho bạn điều này:

CL-USER 2 > (defun foo (n e &aux (z (1+ n))) 
       (loop for i to z 
        unless (= i z) 
         collect i 
        else 
         nconc e)) 
FOO 

CL-USER 3 > (foo 4 '(f o o)) 
(0 1 2 3 4 F O O) 
+0

Tôi chắc chắn vẫn còn nhiều điều cần tìm hiểu, đặc biệt là trên ** vòng lặp ** :-) –

+3

@ Jean-ClaudeArbaut: Vì bạn đã phát hiện ra rằng việc triển khai 'LOOP' theo dõi các khuyết điểm cuối cùng và xác minh macro mở rộng mã , có vẻ như bạn đang khởi đầu tốt. ;-) –

+1

Tôi thích điều này nhiều hơn tôi. Đó là một giải pháp sạch hơn nhiều (chỉ cần thêm một sự lặp lại thêm và làm một cái gì đó đặc biệt), ** và ** bạn nhận được sự trở lại tiềm ẩn của danh sách. Đây là câu trả lời tôi chấp nhận. –

3

Câu hỏi: Có cách nào để sử dụng này "con trỏ" trong mệnh đề cuối cùng? Nó sẽ cho phép một để thêm một cái gì đó cho kết quả, đó là đôi khi hữu ích.

Tôi nghĩ câu trả lời là "không". Cú pháp cho mệnh đề cuối cùng được đưa ra trong:

initial-final::= initially compound-form+ | finally compound-form+ 

Bạn có thể, tất nhiên, thu thập vào một số biến đặc biệt, và sau đó nối hoặc nconc với điều đó:

CL-USER> (loop for i from 1 to 5 
       collect i into ns 
       finally (return (nconc ns (list 'a 'b 'c)))) 
;=> (1 2 3 4 5 A B C) 

Điều đó không liên quan đến một đi bộ thêm thông qua kết quả, mặc dù, có thể không mong muốn. (Tôi nghĩ rằng đó là những gì bạn đang cố gắng để tránh.)

Một lựa chọn khác sẽ được xây dựng trong danh sách sử dụng nconc hơn thu thập. Nếu bạn đang sử dụng thu thập, thì bạn chỉ nhận được một giá trị tại một thời điểm và sau đó thu thập nó. Bạn có thể đặt một giá trị đó vào một danh sách và sau đó "thu thập" nó với nconc. Nếu bạn lưu danh sách một phần tử đó vào một biến trong vòng lặp, bạn có thể tham chiếu đến nó, và nó khá là một con trỏ đuôi. Ví dụ .:

CL-USER> (loop for i from 1 to 5 
      for ilist = (list i) 
      nconc ilist into ns 
      finally (progn 
         (nconc ilist '(a b c)) ; *** 
         (return ns))) 
;=> (1 2 3 4 5 A B C) 

Trong dòng được đánh dấu bằng ***, các cuộc gọi đến nconc chỉ có đi qua giá trị cuối cùng của IList, nghĩa là, (5). Đó là một cuộc gọi nhanh chóng nconc.

+0

Ý tưởng tốt đẹp, để sử dụng nconc. Đó là một giải pháp sạch hơn nhiều. Cảm ơn bạn. –

+0

Nó dường như không hoạt động độc đáo nếu nconcing xảy ra có điều kiện sau một ** khi **. Vẫn có thể thực hiện ** nconc (setf ilist (danh sách i)) thành ns **, nhưng trong trường hợp mệnh đề ** when ** luôn là false, ** ns ** là không ở cuối. Sau đó nó sẽ cần một ** tiếp theo (nếu (null ns) ...) **. Tôi có đúng không? –

+1

@jean tôi nghĩ câu trả lời của Rainer tốt hơn. Hãy xem xét không chấp nhận điều này và chấp nhận của mình. –

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