写経

某氏がJavaScriptで書いたコードをScheme(Gauche)で写経。

native stack版

(use util.list)
(use gauche.test)

(define (make-int i)
  `((type . int)
    (value .  ,i)))

(define (make-add  . args)
  `((type . add)
    (args . ,args)))


(define (ese-eval exp)
  (cond ((eq? (assq-ref exp 'type) 'int)
         (assq-ref exp 'value))
        ((eq? (assq-ref exp 'type) 'add)
         (+ (ese-eval (car (assq-ref exp 'args)))
            (ese-eval (cadr (assq-ref exp 'args)))))
        (else
         (print "not reached"))))

(test-start "ese-eval")

(test* "eval int" 10 (ese-eval (make-int 10)))
(test* "(add 10 12)" 22 (ese-eval (make-add (make-int 10) (make-int 12))))
(test* "(add 10 (add 1 3))" 14 (ese-eval (make-add (make-int 10)
                                                   (make-add (make-int 1) (make-int 3)))))
(test-end)

native stackじゃない版

(use util.list)
(use gauche.test)
(use util.queue)

(define (make-int i)
  `((type . int)
    (value .  ,i)))

(define (make-add  . args)
  `((type . add)
    (args . ,args)))

(define (make-cont  need-eval cont-proc exp)
  `((type      . cont)
    (need-eval . ,need-eval)
    (cont      . ,cont-proc)
    (exp       . ,exp)))

(define (type-eq? exp type)
  (eq? (assq-ref exp 'type) type))

(define (int? exp)
  (type-eq? exp 'int))

(define (cont? exp)
  (type-eq? exp 'cont))

(define (add? exp)
  (type-eq? exp 'add))


(define (ese-eval exp)
  (let ((stack (make-queue)) (ret '()))
    (queue-push! stack exp)
    (let loop ((exp1 (queue-pop! stack)))
      (cond
       ((int? exp1)
        (set! ret (assq-ref exp1 'value)))
       ((add? exp1)
        (queue-push!
         stack
         (make-cont
          #f
          (lambda (second)
            (make-cont #t
                       (lambda (first-arg) (make-int (+ second first-arg)))
                       (car (assq-ref exp1 'args))))
          '()))
        (queue-push! stack (cadr (assq-ref exp1 'args))))
       ((cont? exp1)
        (let ((res ((assq-ref exp1 'cont) ret)))
          (cond ((assq-ref res 'need-eval)
                 (queue-push!
                  stack
                  (make-cont #f (lambda (arg) ((assq-ref res 'cont) arg)) '()))
                 (queue-push! stack (assq-ref res 'exp)))
                ((int? res)
                 (set! ret (assq-ref res 'value)))))))
      (if (zero? (queue-length stack))
          ret
          (loop (queue-pop! stack))))))

(test-start "ese-eval")
(test* "eval int" 10 (ese-eval (make-int 10)))
(test* "(add 10 12)" 22 (ese-eval (make-add (make-int 10) (make-int 12))))
(test* "(add 10 (add 1 3))" 14 (ese-eval (make-add (make-int 10)
                                                  (make-add (make-int 1) (make-int 3)))))
(test-end)

よくよく見ると Named Let はループじゃないので native スタック使うよね。
いやいや結局末尾再帰だから。。みたいな話はおいておいて。

頭が悪いのでまだ完全には理解できていない。
同じものを1週間後に3分で書けといわれたら絶対無理><。