2011-06-28 25 views
9

Tôi nhận ý tưởng này từ XKCD's Hofstadter comic; cách tốt nhất để tạo vòng lặp có điều kiện trong (bất kỳ) phương ngữ Lisp nào thực thi hàm cho đến khi nó trả về NIL tại thời điểm nó thu thập các giá trị trả về vào một danh sách.Thực hiện một hàm cho đến khi nó trả về một con số không, thu thập các giá trị của nó vào một danh sách

Đối với những người chưa xem trò đùa, tự truyện của Douglas Hofstadter "tám từ" chỉ gồm sáu từ: "Tôi So Meta, Ngay cả từ viết tắt này" có chứa phần tiếp theo của trò đùa: (một số meta meta-paraprosdokian?) "Is Meta" - trò đùa là tự truyện thực sự là "Tôi So Meta, Ngay cả Từ viết tắt này là Meta". Nhưng tại sao không đi sâu hơn?

Giả chức năng acronymizing META tạo ra một từ viết tắt từ một chuỗi và tách nó thành các từ, trả NIL nếu chuỗi chứa nhưng một từ:

(meta "I'm So Meta, Even This Acronym") ⇒ "Is Meta" 
(meta (meta "I'm So Meta, Even This Acronym")) ⇒ "Im" 
(meta (meta (meta "I'm So Meta, Even This Acronym"))) ⇒ NIL 

(meta "GNU is Not UNIX") ⇒ "GNU" 
(meta (meta "GNU is Not UNIX")) ⇒ NIL 

Bây giờ tôi đang tìm cách để thực hiện một chức năng để:

(so-function #'meta "I'm So Meta, Even This Acronym") 
⇒ ("I'm So Meta, Even This Acronym" "Is Meta" "Im") 
(so-function #'meta "GNU is Not Unix") 
⇒ ("GNU is Not Unix" "GNU") 

Cách tốt nhất để làm điều này là gì?

+1

Câu trả lời của Jordan "lừa đảo" bằng cách chỉ được kết nối với những trường hợp cụ thể. Với mục đích chung là (meta), bạn sẽ phải quyết định ranh giới từ. Bạn có thể sử dụng một tập tin từ điển. – compman

+0

@compman, câu hỏi của ông cụ thể nói rằng giả sử sự tồn tại của 'meta' vì vậy tôi sẽ không gọi nó là" gian lận ". Tuy nhiên, bạn có một điểm. Tôi không hoàn toàn chắc chắn * làm thế nào * để quyết định nơi ranh giới từ là mặc dù, thậm chí được đưa ra một từ điển. Một vấn đề cụ thể sẽ là cách quyết định bạn đang xem một từ và nên chấm dứt. EDIT: Một cách có thể là để nói "nếu từ này có trong từ điển của tôi hoặc nếu tôi không thể chia từ này thành bất kỳ số lượng subwords nào sao cho tất cả trong từ điển của tôi, chấm dứt". –

Trả lời

3

Điều này thật dễ dàng. Tôi không muốn viết một giải pháp, vì vậy thay vào đó tôi sẽ - nhưng nó sẽ là phiên bản tinh ranh bất ngờ, điều này có thể dẫn đến chứng ngộ bất ngờ nếu bạn sẽ làm theo:

(defun so-function (f str) 
    (let (x '()) 
    (while str (setq x (cons str x)) (setq str (funcall f str))) 
    (reverse x))) 

Để dùng thử bạn sẽ cần nó meta, nhưng tôi không biết làm thế nào bạn muốn quyết định nơi để đặt các không gian, vì vậy thay vì tôi sẽ giả nó:

(defun meta (x) 
    (cadr (assoc x '(("I'm So Meta, Even This Acronym" "Is Meta") 
        ("Is Meta" "Im") 
        ("GNU is Not UNIX" "GNU"))))) 

Điều này làm cho mã mà bạn muốn làm việc. Đối với sự giác ngộ - cố gắng viết nó nên thay vì những gì bạn muốn, so-function sẽ là một hàm bậc cao - một trong đó sẽ làm việc như thế này:

(funcall (so-function #'meta) "GNU is Not UNIX") 

hoặc, trong Đề án:

((so-function meta) "GNU is Not UNIX") 

Các gợi ý lớn ở đây là bạn không thể làm điều đó trong đồng bằng elisp (ít nhất là không có thủ đoạn từ thư viện cl). Để có được tín dụng đầy đủ, tránh các đột biến - điều này sẽ dẫn đến cách tự nhiên bạn muốn viết nó trong Đề án và thậm chí có thể dễ đọc hơn phiên bản setq.

+0

Không nên gọi hàm phiên bản Emacs Lisp của bạn là '(funcall # 'so-function #' meta" GNUS không phải là UNIX ")' trả về '(" GNU không phải là UNIX "" GNU ")'? (và tương tự '(funcall # 'so-function #' meta" Tôi So Meta, Ngay cả Từ viết tắt ")' ⇒ '(" Tôi So Meta, Ngay cả Từ viết tắt "" là Meta "" Im ")') –

+0

Hoặc chỉ là '(tự truyện '# meta tự truyện)' giống như câu trả lời của Jordan Wade –

+0

Không - hãy xem phần đầu của phần đó, nơi tôi đã nói "cố gắng viết nó ..." - đó là bài tập tôi đưa ra! –

1

Ném này lại với nhau và có vẻ như để làm việc:

(defun collect-until-null (function initial-value) 
    "Collects INITIAL-VALUE and the results of repeatedly applying FUNCTION to 
    INITIAL-VALUE into a list. When the result is NIL, iteration stops." 
    (if initial-value 
     (cons initial-value 
      (collect-until-null function (funcall function initial-value))))) 

Sử dụng một phiên bản sửa đổi nhẹ của meta Eli Barzilay đăng,

(defun meta (x) 
    (cadr (assoc x 
       '(("I'm So Meta, Even This Acronym" "Is Meta") 
       ("Is Meta" "Im") 
       ("GNU is Not UNIX" "GNU")) 
       :test #'equal))) ;strings with the same characters aren't EQL 

tôi nhận được kết quả mà bạn đang tìm kiếm.

CL-USER> (collect-until-null #'meta "I'm So Meta, Even This Acronym") 
("I'm So Meta, Even This Acronym" "Is Meta" "Im") 

CL-USER> (collect-until-null #'meta "GNU is Not UNIX") 
("GNU is Not UNIX" "GNU") 

Sửa: @Rainer Joswig chỉ ra rằng collect-until-null sẽ xả ngăn xếp nếu có một chuỗi đủ lớn. Dưới đây là phiên bản lặp của Rainer mà không có vấn đề này.

(defun collect-until-null-iter (function initial-value) 
    "Collects INITIAL-VALUE and the results of repeatedly applying FUNCTION to 
    INITIAL-VALUE into a list. When the result is NIL, iteration stops." 
    (loop for result = initial-value then (funcall function result) 
     while result collect result)) 
+0

tràn ngăn xếp ... –

+0

@Rainer Joswig, bạn có thể cụ thể hơn không? Tôi đã chạy lại nó trong một trường hợp SBCL mới và không có vấn đề gì. –

+0

chỉ cần sử dụng danh sách dài –

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