我正在编写我的第一个程序。我对递归非常深入,因为我基本上是为一个简单的机器人解释一个程序,它可以有嵌套的过程调用。
如果发现违规行为,我需要停止解释程序并返回上一个有效状态。
我通过声明全局变量(define illegalMoveFlag 0)
然后通过set!
设置它来解决它。
它工作正常,但我想我的导师不喜欢它(因为它不是我想的功能方法)
我想到的其他方法是为我在程序中递归调用的每个函数添加一个错误参数。我不太喜欢它,因为它会使我的代码可读性差得多,但我想它更具“功能性”。
有没有第三种方法我没有想到?我的方法可以在这个范例中证明是合理的,还是基本上是代码味道?
答案 0 :(得分:5)
停止递归的常用方法是停止递归。即,不再调用递归函数。 : - )
如果这样做太难了,另一种突破某种方法的方法是捕获顶层的延续(在开始递归之前),然后在需要“转义”时调用延续。但是,您的教练可能不喜欢这种方法。 ; - )
答案 1 :(得分:5)
由于这是你的第一个Scheme程序,你可能只需要引入一个条件表达式cond
,以避免在到达结尾时进一步递归。例如:
; sum : natural -> natural
; compute the sum 0+1+...+max
(define (sum max)
(define (sum-helper i sum-so-far)
(if (> i max)
sum-so-far
(sum-helper (+ i 1) (+ sum-so-far i))))
(sum-helper 0 0))
(display (sum 10))
(newline)
但是,如果您需要在C中返回类似return
的传统longjmp
,则需要存储并使用转义延续。这可以这样做:
(define (example)
(let/ec return
(define (loop n)
(if (= n 100000)
(return (list "final count: " n))
(loop (+ n 1))))
(loop 0)))
(display (example))
如果您的Scheme实施中未定义let/ec
,请在程序前加上:
(define-syntax let/ec
(syntax-rules ()
[(_ return body ...)
(call-with-current-continuation
(lambda (return)
body ...))]))
更新:
请注意,cond
有一个=>
变体:
(cond
[(call-that-can-fail)
=> (lambda (a) <use-a-here>))]
[else <do-something-else>])
如果调用成功,则第一个子句为 采取和结果绑定到。如果呼叫失败, 然后使用else子句。
答案 2 :(得分:2)
您可能希望使用内置过程error
,如下所示:
(error "Illegal move") ; gives ** Error: Illegal move
这会引发异常并停止解释程序(虽然我怀疑这可能不是你想要的)。
您还可以提供其他参数,例如:
(error "Illegal move: " move) ; gives ** Error: Illegal move: <move>
答案 3 :(得分:1)
您可以使用延续退出递归(或从任何其他进程)。在不了解更多细节的情况下,我建议您查看口译员的documentation。
答案 4 :(得分:1)
将illegalMoveFlag设为函数中的参数而不是全局变量
我会给你一个带有阶乘的简单例子
即: 0! = 1 N! = n *(n - 1)!当n(1 ...无穷大)
时让我们称之为递归因子
(define (fact-r n)
(if
[eq? n 0]
1
(* n (fact-r (- n 1)))
)
)
另一种方法是使用函数的参数来结束递归 让我们称之为迭代因子
(define (fact-i n total)
(if
(eq? n 0)
total
(fact-i (- n 1) (* n total))
)
)
总需要从1开始,所以我们应该制作另一个功能,以便更好地使用它
(define (nice-fact n)
(fact-i n 1))
您可以使用illegalMoveFlag执行类似操作以避免使用全局变量 至于避免使用套装!我们可能需要更多信息。
在某些情况下,仍然很难避免使用它。 Scheme完全图灵完整,无需使用set!但是,当涉及到访问外部信息源(如数据库或机器人集)时!可以成为唯一切实可行的解决方案......