2013-05-02 4 views
0

"명령"과 "프로그램"(미로에 거주하는 로봇에 대한 우리 교수가 발명 한 "로봇 언어"의 해석자)의 목록을 재귀 적으로 분석해야하는 프로그램을 작성 중입니다. 초기 구현이 너무 느리기 때문에 call-with-current-continuation을 사용하기로 결정했습니다.스키마 호출/cc 문제 - exit 구현.

나는 call/cc가 어떻게 작동하는지 알고있다. 설명으로 thisthis을 읽었다.

내 전화/CC는 튜토리얼의이 부분을 기반으로합니다

는 종종 우리가 사용하고자하는 통화와 전류 연속 탈출 절차 이외의 인수를 일부 프로 시저를 호출 할 수 있습니다.

(정의 (foo는 XY 탈출) ... (만약 (= X 0) (탈출 'ERROR)) : 예를 들어, 우리는 외에 두 개의 인수하여 이스케이프 절차를 취하는 절차가있을 수 있습니다. ..)) 우리는 프로 시저를 currying하여이를 해결할 수 있습니다. 프로 시저를 하나의 인수로 만듭니다.

[이전 장에는 currying에 대한 토론이 있어야합니다! ]

x 및 y의 값으로 0과 1을 전달하고 이스케이프 프로 시저를 전달하는 것으로 가정합니다. 오히려

말보다 (통화와 전류 연속 foo는) foo에 대한 호출에 충분한 인수를 전달하지 않습니다, 우리는 말을

(통화와 전류 연속 (람다 (탈출) (foo 0 1 escape))) 람다 식은 정확히 우리가 수행하는 클로저를 만듭니다. . 인수 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

[1] 라켓에서 사용하는 언어는 무엇입니까? (나는 당신이 DrRacket을 사용하고 있다고 가정합니까?) 또한, 어떤 버전의 Racket을 사용하고 있습니까?; [2] 동일한 버그를 보여주는 직접 실행할 수있는 작은 버전의 예제를 파생시킬 수 있습니까? (어떤면에서는 작은 독립적 인 테스트를 구축하는 것에 대한 필자의 기본 조언은 여전히 ​​여기에 적용됩니다.) – pnkfelix

+0

@pnkfelix - DrRacket, language Scheme, standard R5RS를 사용합니다. 모든 테스트를 시도했지만 지금까지는 내 프로그램에 어플라이언스가 적용되지 않습니다. 나는 내가 추측하는 더 많은 시간이 필요해. – petajamaja

+0

나쁜 소식. 꼬리 - 재귀를 사용하지 않았으므로 여기서는/cc를 사용하면 전혀 쓸모가 없습니다. – petajamaja

답변

2

새로운 후속 조치 :이 시점에서 나는 포스터의 질문에 의아해합니다. GoZoner는 call/cc에 의해 전달 된 연속이 실제 매개 변수를 요구할 수 있다고 지적했지만, 이는 Racket (포스터의 오류 메시지를 기반으로, 내가 논의중인 것으로 가정하는 언어 구현)에 일반적으로 해당되지 않습니다.

또한 질문에 넣은 원래 포스터가 불완전한 코드 스 니펫이므로 문제를 복제하기 위해 코드를 직접 실행할 수 없습니다. (나의 비공식적 인 분석은 원래 포스터가 제시 한 call-with-current-continuation의 사용에있어서 명백한 버그를 드러내지 않았다.) 원래의 포스터가 동일한 문제를 드러내는 최소 (또는 적어도 더 작은) 테스트 케이스를 도출 할 수 있다면 유용 할 것이다. .

라켓 내의 특정 언어 또는 언어 수준 중 하나가 call/cc의 더 제한적인 형식을 사용하고 있지만 이러한 언어 수준의 증거를 찾지 못했을 수 있습니다.나는 원래의 포스터에 질문을 제기 할 것이다.


편집 : 크리스 - 농담하는 젊은 내 해설 (이 답변에 대한 의견을 참조) 여기에 적절하게 적용되지 않을 수도 있다고 지적했다. 라켓이 명령형 코드의 연속 처리를보다 신중하게 조사해야합니다. 아래의 메모는 Asker에게 잘못된 경로를 알려줍니다. (내 노트가 위조라는 것을 확인할 수 있다면이 대답을 삭제할 계획입니다.)

후속 편집 : call/cc의 Racket 처리는 실제로 호출 될 때 0 값을 허용하는 연속을 따르는 것으로 보입니다 그 값을 파기하는 문맥으로부터. 예 :

(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) 
,536

가 실행되며로부터의 출력과 상기 실행의 출력을 비교

각각 무엇을 설명하고 있는지주의 깊게 생각하고 프로그램의 경우 어떻게 중요할지 생각해보십시오.

+0

나는 동의하지 않는다. 나는 여기서'(exit)'가 완벽하게 합리적이라고 생각한다. 'call/cc'가 0 값을 반환하도록합니다. 그 결과가 버려지기 때문에 이것은 괜찮습니다. OP는 명령형 프로그래밍을 수행하므로, 수행되는 모든 작업은 반환 값이 아닌 부작용에 관한 것임을 기억하십시오. –

+0

글쎄, 순수한 기능 스타일로 시도했지만 경험 부족으로 인해 모든 시도가 실패했습니다. 그래서 여기에 반드시 명령형과 기능적 스타일이 혼합되어 있습니다. – petajamaja

+0

@ ChrisJester-Young'simulate'가 어떻게 호출되는지 예제를 보지 못했기 때문에 반환 값이 무시된다는 것을 알 수 있다고 생각하지 않습니다. 그러나 아마 당신은 내가 끝까지 실종하고있는 계약 위반으로부터의 비난 과제에서 무엇인가를보고있을 것입니다. – pnkfelix

1

call/cc '이스케이프 프로 시저'는 단일 인수를 필요로합니다. 필수 인수없이 (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. 

'이스케이프 절차는'값이 call/cc이 모든 계획 기능처럼 값을 생성 할 필요가 있다는 점이다 기대하는 이유.제공되는 인수는 이스케이프 프로 시저가 호출 될 때 call/cc의 반환 값입니다.