我正在编写一个需要递归分析“命令”和“程序”列表的程序(它是我们的教授为生活在迷宫中的机器人发明的一些“机器人语言”的翻译)。因为我的初始实现太慢了,所以我决定使用call-with-current-continuation。
我知道call / cc是如何工作的,我已阅读this和this作为解释。
我的电话/ cc基于教程的这一部分:
我们通常希望使用call-with-current-continuation来调用一些 获取除转义过程之外的参数的过程。对于 例如,我们可能有一个除了两个参数的过程 逃避程序,因此:
(define(foo x y escape)...(if(= x 0) (转义'错误))...))我们可以通过讨论程序来解决这个问题,使其成为一个参数的过程。
[前一章应讨论讨论! ]
假设我们想要传递0和1作为x和y的值,以及 把foo换成逃生程序。而不是说
(call-with-current-continuation foo)没有通过 调用foo的参数,我们说
(call-with-current-continuation(lambda(escape)(foo 0 1 escape))) lambda表达式创建一个完全符合我们的闭包 想。它将使用参数0,1和转义过程调用foo 通过call-with-current-continuation创建。
但是,由于某些原因它不起作用并抛出此异常:
call-with-current-continuation: contract violation
expected: (any/c . -> . any)
given: #<procedure:...mazesimulator.ss:301:34>
我希望你帮助我找出错误并解释为什么会发生......
以下是与此问题相关的代码部分:
; main program
(define (simulate state expression-list program limit)
; read the input and set global variables
(set! current-orientation (list-ref state 2))
(set! current-coordinates (list-ref state 1))
(set! current-maze (list-ref state 0))
; call the inner function
(call-with-current-continuation (lambda (exit)
(command state expression-list program limit exit)))
; this is the output
(list list-of-executed-commands (list current-maze current-coordinates current-orientation))
)
;; main recursive function
;; analyses expression-list parameter
;; evaluates its elements
;; and calls itself on the cdr of the espression-list
(define (command state expression-list program limit exit)
(if (and (not (null? expression-list))(equal? stop-command #f))
; recursion end condition, the whole procedure will be done only
; if the list is still not empty
(if (atom? expression-list) ;if the list consists of only one command
(if (equal? stop-command #f) ;positive branch - if there were no erros before
(atomic-command state expression-list program limit exit) ;call atomic-command on this element
;when flag is set to #t
(exit))
; here comes a problem with "inner ifs"
(if (atom? (car expression-list)) ;negative branch - if the first element is "if"
(if (equal? (car expression-list) 'if) ;if the list consisits only of if-clause, no other commands ((if ...))
(if ((name->function (list-ref expression-list 1))) ;evaluate the boolean - wall? north? and choose corresponding branch
(command state (list-ref expression-list 2) program limit exit)
(command state (list-ref expression-list 3) program limit exit))
(evaluate-first-and-call-command-on-rest expression-list program limit exit))
(if (equal? (car(car expression-list)) 'if) ;if the if-clause is not the only element in list - "inner if" ((if ...) turn-left put-mark)
(begin ;not only evaluate if-clause,
(if ((name->function (list-ref (car expression-list) 1)))
(command state (list-ref (car expression-list) 2) program limit exit)
(command state (list-ref (car expression-list) 3) program limit exit))
(command state (cdr expression-list) program limit exit)) ;but also call command on cdr!
(evaluate-first-and-call-command-on-rest expression-list program limit exit))))
;when limit is exceeded or when the flag is set to #t
(exit) ))
答案 0 :(得分:2)
新的跟进:所以在这一点上我对海报的问题感到困惑。 GoZoner已经指出call/cc
传递的延续可能在调用时需要一个实际参数,但在Racket中这通常不正确(基于海报的错误信息,我假设正在讨论的语言实现) )。
此外,原始海报放入问题的代码片段不完整,因此无法直接执行代码以尝试复制问题。 (我的非正式分析没有揭示原始海报提出的使用call-with-current-continuation
的明显错误。)如果原始海报可以得出一个最小(或至少更小)的测试用例,那将是有用的。暴露了同样的问题。
可能是Racket中的某个特定语言或语言级别使用了更严格的call/cc
形式,但我没有找到这种语言级别的证据。我将向原始海报提出问题。
编辑:Chris-Jester Young指出我的评论可能不适用于此(请参阅对此答案的评论)。我需要更仔细地调查Racket对强制性代码中延续的处理;下面的注释可能会引导提问者走错路。 (如果我能确认下面的笔记是假的,我打算删除这个答案。)
后续编辑:看来Racket对call/cc
的处理确实传递了一个延续,当从丢弃该值的上下文调用它时,该延续将接受零值。例如:
(define global 7)
(define (goner exit)
(set! global 11)
(exit)
(set! global 13)
(* 2 3))
(define (hi)
(define x global)
(call-with-current-continuation goner)
(* 5 x global))
(* 5 7 11)
(hi)
以上打印385(两次);一次用于(* 5 7 11)
,一次用于(hi)
。
(原评论如下)
您使用零参数调用(exit)
这一事实让我认为您并不完全理解call/cc
如何工作(如在其可观察行为中),尽管您声称相反。< / p>
在您尝试将call/cc
合并到解决方案中之前,我建议您使用一些小例子,完全独立于教授的机器人迷宫基础设施。
例如,我可以通过这种方式轻松重现您的错误消息:
(define (goner)
(* 2 3))
(define (hi)
(let ((x (call-with-current-continuation goner)))
(* 5 x)))
(hi)
从上面我得到:
call-with-current-continuation: contract violation
expected: (any/c . -> . any)
given: #<procedure:goner>
这与您的错误消息非常相似,不是吗? (虽然说实话,这可能只是巧合)。
将上面运行的输出与运行的输出进行比较:
(define (goner exit)
(* 2 3))
(define (hi)
(let ((x (call-with-current-continuation goner)))
(* 5 x)))
(hi)
和
(define (goner exit)
(* 2 (exit)))
(define (hi)
(let ((x (call-with-current-continuation goner)))
(* 5 x)))
(hi)
和
(define (goner exit)
(* 2 (exit 3)))
(define (hi)
(let ((x (call-with-current-continuation goner)))
(* 5 x)))
(hi)
和
(define (goner exit)
(* (exit 2) 3))
(define (hi)
(let ((x (call-with-current-continuation goner)))
(* 5 x)))
(hi)
仔细考虑每个人所说明的内容,并考虑一下你的计划可能会如何重要。
答案 1 :(得分:1)
call / cc'vent procedure'需要一个参数。您在没有必需参数的情况下将其称为(exit)
。不同的Scheme实现将以不同方式处理它。这是抱怨的一个:
1 ]=> (define (doit exit)
(display "DOIT2\n")
(exit)) ;; no argument, expect error
;Value: doit
1 ]=> (define (try)
(call-with-current-continuation
(lambda (exit) (display "TRY\n") (doit exit)))
'done)
;Value: try
1 ]=> (try)
TRY
DOIT2
;The procedure #[continuation 13] has been called with 0 arguments; it requires exactly 1 argument.
'escape procedure'期望值的原因是call/cc
与所有Scheme函数一样,需要生成一个值。提供的参数是调用转义过程时call / cc的返回值。