2011-06-28 43 views
9

我從XKCD's Hofstadter comic得到了這個想法;在(任意)Lisp方言中創建條件循環的最佳方式是執行函數,直到它返回NIL,此時它將收集的返回值收集到列表中。執行一個函數,直到它返回一個零,將其值收集到一個列表中

對於那些沒有見過這個笑話的人來說,道格拉斯霍夫斯塔特的「八字」自傳只包含六個字:「我是如此元,甚至是這個首字母縮寫」,包含了這個笑話的延續:奇怪的元paraprosdokian?)「是元」 - 這個笑話是,自傳實際上是「我是元,甚至這個首字母縮略詞是Meta」。但爲什麼不更深入?

假設acronymizing功能META從一個字符串創建一個縮寫詞,它分裂成的話,返回NIL如果字符串包含只有一個詞:

(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 

現在我正在尋找如何實現功能所以:

(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") 

這樣做的最好方法是什麼?

+1

約旦的答案「欺騙」通過僅針對特定情況進行連線。對於一般用途(meta),您將不得不決定單詞邊界在哪裏。你可以使用一個字典文件。 – compman

+0

@compman,他的問題具體說是假設'meta'的存在,所以我不會稱之爲「作弊」。不過,你有一點。我不太確定*如何判斷詞邊界的位置,即使給出了字典。一個特定的問題是如何決定你正在看一個單詞,並應該終止。 編輯:一種方式可能是說「如果這個詞在我的詞典中,或者如果我不能將這個詞分成任何數量的子詞,這樣他們都在我的詞典中,終止」。 –

回答

3

這很簡單。我不想寫一個解決方案,所以不是我會 - 但它會是糟糕的elisp版本,這可能會導致意想不到的啓示,如果你堅持到底:

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

要嘗試了這一點你需要的是meta,但我不知道你會如何決定在哪裏放置的空間,所以不是我會僞造它:

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

這使得想要工作的代碼。至於啓示 - 儘量把它寫這樣,而不是你想要什麼,so-function將是一個高階函數 - 一個會像這樣:

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

,或者在計劃:

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

這裏最大的提示是你不能用簡單的elisp來做(至少不能沒有cl庫中的技巧)。要獲得完整的功勞,請避免這些突變 - 這會導致您在Scheme中編寫它的自然方式,甚至可能看起來比setq版本更具可讀性。

+0

不應該將Emacs Lisp版本函數調用爲'(funcall#'so-function#'meta「GNUS不是UNIX」)'它返回'(「GNU is Not UNIX」「GNU」)'? (和類似的'(funcall#'so-function#'meta'我是如此元,甚至這個首字母縮寫詞')'⇒'(「我是元,甚至這個首字母縮寫詞」「是Meta」「Im」)' ) –

+0

或者只是'(so-function#'meta autobiography)'就像約旦韋德的回答 –

+0

不 - 看到那部分的開始,我說「試着寫這麼...」 - 這是練習I給出了! –

1

拋出此一起,似乎工作:

(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))))) 

使用meta禮Barzilay略加修改的版本發佈,

(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 

我得到你正在尋找的結果。

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") 

編輯:@Rainer Joswig指出,如果給予足夠大的順序collect-until-null將耗盡堆棧。下面是沒有這個問題的Rainer迭代版本。

(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

堆棧溢出... –

+0

@Rainer Joswig,你能更具體嗎?我重新運行它在一個新的SBCL實例中,並沒有問題。 –

+0

只是使用一個長列表 –

相關問題