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

Lisp 执行函数,直到返回nil,并将其值收集到列表中,lisp,elisp,common-lisp,Lisp,Elisp,Common Lisp,我的想法来自于;用(任何)Lisp方言创建条件循环的最佳方法是什么?该循环执行函数,直到返回NIL,然后将返回的值收集到列表中 对于那些没有看过这个笑话的人来说,据说道格拉斯·霍夫斯塔特的“八字”自传只有六个字:“我太元了,甚至这个首字母缩略词”包含了这个笑话的延续:(一些奇怪的元paraprodokian?)“是元的”——这个笑话是自传实际上是元的“我是如此的Meta,甚至这个首字母缩略词也是Meta”。但为什么不更深入一点呢 假设首字母缩略词化函数META从字符串创建首字母缩略词并将其拆分为

我的想法来自于;用(任何)Lisp方言创建条件循环的最佳方法是什么?该循环执行函数,直到返回
NIL
,然后将返回的值收集到列表中

对于那些没有看过这个笑话的人来说,据说道格拉斯·霍夫斯塔特的“八字”自传只有六个字:“我太元了,甚至这个首字母缩略词”包含了这个笑话的延续:(一些奇怪的元paraprodokian?)“是元的”——这个笑话是自传实际上是元的“我是如此的Meta,甚至这个首字母缩略词也是Meta”。但为什么不更深入一点呢

假设首字母缩略词化函数
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")

做这件事的最佳方法是什么?

这很简单。我不想写解决方案,所以我会写——但它将是蹩脚的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函数
将是一个更高阶的函数——一个这样工作的函数:

(funcall (so-function #'meta) "GNU is Not UNIX")
或者,在方案中:

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

这里的一个重要提示是,你不能用普通的elisp(至少不能没有
cl
库中的技巧)。要获得全部学分,请避免突变——这将导致你用Scheme编写它的自然方式,甚至可能比
setq
版本更具可读性。

将这一点结合在一起,似乎有效:

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

Edit:@Rainer Joswig指出,如果给定足够大的序列,
collect直到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))

您的Emacs Lisp版本函数调用不应该是
(funcall#'so-function#'meta“GNUS不是UNIX”)
,它返回
(“GNU不是UNIX”“GNU”)
?(类似地
(funcall#'so-function#'meta“我是如此meta,甚至这个首字母缩写)
(“我很元,甚至这个首字母缩略词”也是元“Im”)
)或者只是
(所以功能#元自传)
就像Jordan Wade的回答中那样——看那部分的开头,我说“试着写吧…”--这就是我给出的练习!@Rainer Joswig,你能说得更具体一点吗?我在一个新的SBCL实例中重新运行了它,没有任何问题。@Rainer,这并不能帮助我找出问题所在。你能发布你正在使用的导致溢出的确切代码吗?(或者链接,如果它太长,无法发表评论吗?)(收集到null(let((n 20000))(lambda(o)(除非(zeropn)(decf n)))t您可能需要为您的Lisp实现更改n。我测试它的那一个有n=2300的堆栈溢出。@Rainer,我现在知道了,并用(我认为是)尾部递归版本编辑了答案。您能检查一下并告诉我(1)是否正确吗你的Lisp在新版本中耗尽了堆栈,(2)如果我做的是非惯用的事情,我是自学成才的,所以如果我做了一些能让更有经验的人去“WTF”的事情,我也不会感到惊讶。这与你的代码一样,不需要稍微非标准的TCO支持:(loop for result=obj)(funcall func result)而result collect result)。还要注意的是,与您的版本存在相同的问题:初始值被收集-没有应用到它的函数,这是没有文档记录的。Jordan的答案“欺骗”只针对特定情况。对于通用
(meta)
,你必须决定词的边界在哪里。你可以使用字典文件。@compman,他的问题特别提到假设存在
meta
,所以我不会称之为“作弊”.不过,你还是有道理的。即使给我一本字典,我也不太确定如何确定单词的边界。一个特别的问题是如何确定你在看一个单词并且应该终止。编辑:一种方法可能是“如果这个词在我的字典中,或者如果我不能将这个词拆分成任意数量的子词,以便它们都在我的字典中,那么终止”。