在实现“goto”时,首先要问什么是goto。虽然看起来很明显,但大多数人并没有考虑 goto 与函数堆栈的关系。
如果您在函数内部执行“goto”,实际上就是放弃了函数调用堆栈。这被认为是不好的做法,因为函数堆栈的设计期望您在委派中间任务后从中断的地方继续。这就是为什么goto用于异常,异常可以用来模拟goto,这个我会解释。
有限状态机可能是 goto 的最佳用例,大多数情况下它是通过循环和 switch 语句以笨拙的方式实现的,但我相信“顶级” goto 是最干净、最语义化的方式实现有限状态机。在这种情况下,您要确保,如果您有更多变量,它们是全局变量,并且不需要封装。确保首先对变量状态空间进行建模(可能不同于执行状态,即有限状态机)。
我相信使用 goto 有合理的设计理由,异常处理是一种特殊情况,将 goto 与函数混合是有意义的。但是,在大多数情况下,您希望将自己限制为“顶级” goto,因此您永远不会在函数内调用 goto,而只能在全局范围内调用。
在现代语言中模拟顶级 goto 的最简单方法是实现顶级 goto,只需要全局变量和一个空的调用堆栈。因此,为了保持调用堆栈为空,每次调用新函数时都会返回。这是打印前 n 个斐波那契数的示例:
a = 0
b = 1
n = 100
def A():
global a, b
a = a + b
n -= 1
print(a)
return B() if n > 0 else 0
def B():
global a, b
b = a + b
n -= 1
print(b)
return A() if n > 0 else 0
A()
虽然这个例子可能比循环实现更冗长,但它也更加强大和灵活,并且不需要特殊情况。它让您拥有一个完整的有限状态机。您也可以使用 goto 运行器对其进行修改。
def goto(target):
while(target) target = target()
def A():
global a, b
a = a + b
print(a)
return B
def B():
global a, b
b = a + b
print(b)
return A
goto(A)
要强制执行“返回”部分,您可以编写一个 goto 函数,该函数在完成时简单地抛出一个异常。
def goto(target):
target()
throw ArgumentError("goto finished.")
def A():
global a, b
a = a + b
print(a)
goto(B)
def B()
global a, b
b = a + b
print(b)
goto(A)
goto(A)
所以你看,很多这都是过度思考,而一个调用函数然后抛出错误的辅助函数就是你所需要的。您可以进一步将其包装在“开始”函数中,以便捕获错误,但我认为这不是绝对必要的。虽然其中一些实现可能会用完您的调用堆栈,但第一个运行器示例将其保持为空,如果编译器可以进行尾调用优化,那也会有所帮助。