Scheme call / cc issue - 实现退出。

时间:2013-05-02 23:36:50

标签: scheme callcc

我正在编写一个需要递归分析“命令”和“程序”列表的程序(它是我们的教授为生活在迷宫中的机器人发明的一些“机器人语言”的翻译)。因为我的初始实现太慢了,所以我决定使用call-with-current-continuation。

我知道call / cc是如何工作的,我已阅读thisthis作为解释。

我的电话/ 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) ))

2 个答案:

答案 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的返回值。