【问题标题】:Executes a function until it returns a nil, collecting its values into a list执行一个函数直到它返回一个 nil,将它的值收集到一个列表中
【发布时间】:2011-09-24 12:54:41
【问题描述】:

我从XKCD's Hofstadter comic 得到这个想法;在(任何)Lisp 方言中创建一个条件循环的最佳方法是什么

对于那些没有看过这个笑话的人来说,道格拉斯·霍夫施塔特的“八个字”自传只有六个字:“I'm So Meta, Even This Acronym”,其中包含笑话的延续:(有些奇怪meta-paraprosdokian?)“Is Meta”——开玩笑的是自传实际上是“我是如此 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")

最好的方法是什么?

【问题讨论】:

  • 乔丹的回答“作弊”,仅针对特定情况进行接线。对于一般用途的 (meta),您必须确定单词边界的位置。您可以使用字典文件。
  • @compman,他的问题专门说假设存在meta,所以我不会称之为“作弊”。不过,你说得有道理。我不太确定 how 来决定单词边界的位置,即使给定了一本字典。一个特殊的问题是如何确定您正在查看一个单词并应该终止。编辑:一种方法可能是说“如果这个词在我的字典里,或者如果我不能把这个词分成任意数量的子词,这样它们都在我的字典里,终止”。

标签: lisp elisp common-lisp


【解决方案1】:

这很容易。我不想写解决方案,所以我会——但它会是糟糕的 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 版本更具可读性。

【讨论】:

  • 您的 Emacs Lisp 版本函数调用不应该是返回 ("GNU is Not UNIX" "GNU")(funcall #'so-function #'meta "GNUS is Not UNIX") 吗? (同样(funcall #'so-function #'meta "I'm So Meta, Even This Acronym")("I'm So Meta, Even This Acronym" "Is Meta" "Im")
  • 或者只是(so-function #'meta autobiography)就像乔丹韦德的回答
  • 不——看那部分的开头,我说“试着写成这样……”——这就是我给出的练习!
【解决方案2】:

把它放在一起,它似乎工作:

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

使用metaEli 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))

【讨论】:

  • @Rainer Joswig,你能更具体一点吗?我在一个新的 SBCL 实例中重新运行它,没有任何问题。
  • @Rainer,这并不能帮助我找出问题所在。您能否发布您正在使用的导致溢出的 exact 代码? (如果评论太长,或者链接?)
  • (collect-until-null (let ((n 20000)) (lambda (o) (unless (zerop n) (decf n)))) t) 您可能需要为您的Lisp 实现。我测试它的那个有 n = 2300 的堆栈溢出。
  • @Rainer,我现在明白了,并用(我认为是)尾递归版本编辑了答案。你能检查一下并告诉我(1)你的 Lisp 是否用新版本耗尽了堆栈,以及(2)我是否在做任何非惯用的事情?我是自学成才的,所以如果我做了一些能让更有经验的人去“WTF”的事情,我不会感到惊讶。
  • 这与您的代码相同,不需要稍微不标准的 TCO 支持: (loop for result = obj then (funcall func result) while result collect result) 。另请注意,与您的版本存在相同的问题:初始值被收集 - 没有应用函数,这不是记录的内容。
猜你喜欢
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多