首页 文章

Scheme call / cc issue - 实现退出 .

提问于
浏览
0

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

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

我的电话/ cc基于这部分教程:

通常我们希望使用call-with-current-continuation来调用一些带有转义而不是转义过程的过程 . 例如,我们可能有一个除了转义过程之外还带有两个参数的过程,因此:(define(foo xy escape)...(if(= x 0)(escape'ERROR))...))我们可以修复通过讨论程序,使其成为一个参数的过程 . [前一章应讨论讨论! ]假设我们想要传递0和1作为x和y的值,以及将foo传递给转义过程 . 我们说(调用with-current-continuation(lambda(escape)(foo 0 1 escape))而不是说(call-with-current-continuation foo)没有传递足够的参数来调用foo . 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 回答

  • 1

    新的后续行动:所以在这一点上我对海报的问题感到困惑 . 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 如何工作(如在其可观察的行为中),尽管你声称相反 .

    在您尝试将 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)
    

    仔细考虑每个人所说明的内容,并考虑一下你的程序如何重要 .

  • 2

    call / cc 'escape 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一样功能,需要产生 Value . 提供的参数是调用转义过程时call / cc的返回值 .

相关问题