执行函数,直到它返回nil,将其值收集到列表中

Ice*_*ack 9 lisp elisp common-lisp

我从XKCD的Hofstadter漫画中得到了这个想法; 什么是在(任何)Lisp方言中创建条件循环的最佳方法,该方法执行函数,直到它返回NIL,同时它将返回的值收集到列表中.

对于那些没有看过这个笑话的人来说,道格拉斯·霍夫施塔特的"八字"自传只包含六个词:"我就是这样的元,甚至这个缩写",包含了这个笑话的延续:(一些奇怪的meta-paraprosdokian ?)"是元" - 笑话是自传实际上是"我是如此元,甚至这个缩写是元".但为什么不深入呢?

假设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
Run Code Online (Sandbox Code Playgroud)

现在我正在寻找如何实现一个功能,以便:

(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")
Run Code Online (Sandbox Code Playgroud)

这样做的最佳方法是什么?

Eli*_*lay 3

这很容易。我不想写一个解决方案,所以我会——但这将是蹩脚的 elisp 版本,如果你坚持下去,这可能会带来意想不到的启发:

(defun so-function (f str)
  (let (x '())
    (while str (setq x (cons str x)) (setq str (funcall f str)))
    (reverse x)))
Run Code Online (Sandbox Code Playgroud)

要尝试这个,你需要那个meta,但我不知道你如何决定将空格放在哪里,所以我会假装它:

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

这使得您想要的代码可以工作。至于启示——尝试写它,而不是你想要的,so-function它将是一个高阶函数——一个像这样工作的函数:

(funcall (so-function #'meta) "GNU is Not UNIX")
Run Code Online (Sandbox Code Playgroud)

或者,在方案中:

((so-function meta) "GNU is Not UNIX")
Run Code Online (Sandbox Code Playgroud)

这里最大的提示是你不能用普通的 elisp 来做到这一点(至少不能没有图书馆的技巧cl)。为了获得充分的积分,请避免突变——这将导致您在Scheme中以自然的方式编写它,甚至可能看起来比版本更具可读性setq