Recursion 返回Lisp中递归函数的顶级调用

Recursion 返回Lisp中递归函数的顶级调用,recursion,lisp,common-lisp,control-flow,Recursion,Lisp,Common Lisp,Control Flow,我有一个递归函数,它需要递归直到找到一个特定的结果。然而,在我的函数体中,在第一次递归调用之后,我可能会进行一些其他计算,或者可能再次递归。但是,如果我递归并找到我正在寻找的结果,那么我希望停止我一直在做的任何递归并返回该结果,以避免进行不必要的计算 在一个普通的递归调用中,一旦到达“基本情况”,它返回给调用的函数,然后返回给调用它的函数,依此类推。我想知道如何返回到函数第一次被调用时,而不必为所有这些中间步骤返回某些内容 对于我的基本递归,我可以编写如下函数: (defun recurse (

我有一个递归函数,它需要递归直到找到一个特定的结果。然而,在我的函数体中,在第一次递归调用之后,我可能会进行一些其他计算,或者可能再次递归。但是,如果我递归并找到我正在寻找的结果,那么我希望停止我一直在做的任何递归并返回该结果,以避免进行不必要的计算

在一个普通的递归调用中,一旦到达“基本情况”,它返回给调用的函数,然后返回给调用它的函数,依此类推。我想知道如何返回到函数第一次被调用时,而不必为所有这些中间步骤返回某些内容

对于我的基本递归,我可以编写如下函数:

(defun recurse (x)
   (if (= x 10)
       (return-from recurse x)
       (progn (recurse (+ x 1)) (print "Recursed!")))))
(recurse 1)
它是为了说明我所说的函数在递归调用后运行更多计算的意思。而且,正如我所写的,这甚至没有返回我感兴趣的值,因为我在返回了我关心的值之后进行了一些打印。(注意:return from命令在这里是无关的,因为我可以在它的位置写“x”。它只是用来在我下面的第二个示例中尝试返回顶级递归时绘制平行线。)

现在,如果我想放弃所有那些额外的“递归”打印,我可以将所有内容封装在一个块中,然后返回到该块:

编辑:这是我的原始示例的函数包装器。这个例子现在应该更清楚了

(defun recurse-to-top (start)
  (block top-level
    (labels ((recurse (x)
               (if (= x 10)
                   (return-from top-level x)
                   (progn (recurse (+ x 1)) (print "Recursed!")))))
      (recurse start))))

运行这个块一直运行到10“被找到”,然后从顶级块返回,没有多余的打印,就像我想要的那样。但是,这似乎是一种获得此功能的非常笨拙的方法。我想知道是否有一种标准的或“最佳”的方式来获得这种行为。

你想要某种非本地的出口。有几种选择:
返回,
转到
抛出
信号

也许有一些变化

(defun recurse (x &optional (tag 'done))
  (catch tag
    (when (= x 10)
      (throw 'done x))
    (recurse (1+ x) nil)
    (print "Cursed!")))
我相信这是你想要的,虽然可能会有很多不必要的捕捉

和Lisp一样,您可以想象有一种完美的语言来解决您的问题,并用这种语言编写程序。比如说

(defun recurse (x)
  (top-level-block recurse
    (when (= x 10)
      (return-from-top-level recurse x))
    (recurse (1+ x))
    (print "Cursed!")))
然后,只需编程即可实现新宏
顶级块
从顶级返回

示例代码如下:

(defmacro top-level-block (name &body body)
  `(if (boundp ',name)
       (progn ,@body)
       (catch ',name
         (let ((,name t))
           (declare (special ,name))
           ,@body))))

(defmacro return-from-top-level (name value)
  `(throw ',name ,value))

DEFUN
已设置词法块:

(defun recurse (start)
  (labels ((recurse-aux (x)
             (case x
               (10 (return-from recurse x))
               (15 x)
               (otherwise
                 (recurse-aux (+ x 1))
                 (print "Recursed!")))))
    (recurse-aux start)))
较早使用的是
捕获
抛出
,这是一种更具动态性的构造,因此允许跨函数退出:

(defun recurse (start)
  (catch 'recurse-exit
    (recurse-aux start)))

(defun recurse-aux  (x)
  (case x
    (10 (throw 'recurse-exit x))
    (15 x)
    (otherwise
     (recurse-aux (+ x 1))
     (print "Recursed!")))))
      (recurse-aux start))))

正如Lars所提到的,有更多的方法可以像这样编程控制流

在您使用块的示例中,“Recursed!”从未打印过,那么为什么它首先出现在那里呢?我相信你的真实代码中还有一些东西在你的示例中丢失了。例如,
(defun recurse(x)(if(=x10)x(recurse(+x1)))
正是你想要的。你误解了我的问题。函数内部的打印要点是演示一个递归函数,它可能在递归调用后执行操作。在第一个示例中,您可以看到每个print函数都会运行,因为它们发生在每次递归返回之后。编写第二个示例是为了展示一种绕过此问题的潜在方法,即返回递归调用的顶层,从而不执行任何进一步的命令。我把print命令放在那里正是为了表明在那个例子中它不会运行,但在第二个例子中它永远不会执行,即使是小于10的值,所以它基本上是死代码。为什么要跳过死代码?我认为你这里有一个XY问题,在这种情况下,更多的代码会更好。我认为你太关心实际的代码,而不是它所代表的概念。在一个普通的递归调用中,一旦到达返回给调用它的函数的“基本情况”,每个返回只会上升一个递归级别,然后发生另一个返回,直到原始调用。(这是第一个例子。)我想知道一旦找到我的“基本情况”,如何在一个步骤中从原始调用返回。示例2显示了一种(不好的)方法,使用第一个示例中的相同函数来模拟此想法。第一个示例运行良好,但由于您提到了多种方法,是否有一种特定的“最佳”或“首选”方法来获得此控制流?@Nyles:第一种方法对于嵌套函数很常见。