Search code examples
schemepostfix-notationmit-scheme

Problems about Scheme with postfix


Here is my code about postfix in scheme:

(define (stackupdate e s)
  (if (number? e)
    (cons e s)
    (cons (eval '(e (car s) (cadr s))) (cddr s))))
(define (postfixhelper lst s)
  (if (null? lst)
    (car s)
    (postfixhelper (cdr lst) (stackupdate (car lst) s))))
(define (postfix list)
   (postfixhelper list '()))
(postfix '(1 2 +))

But when I tried to run it, the compiler said it takes wrong. I tried to check it, but still can't find why it is wrong. Does anyone can help me? Thanks so much! And this is what the compiler said:

e: unbound identifier;
 also, no #%app syntax transformer is bound in: e

Solution

  • eval never has any information about variables that some how are defined in the same scope as it is used. Thus e and s does not exist. Usually eval is the wrong solution, but if you are to use eval try doing it as as little as you can:

    ;; Use eval to get the global procedure
    ;; from the host scheme
    (define (symbol->proc sym)
      (eval sym))
    

    Now instead of (eval '(e (car s) (cadr s))) you do ((symbol->proc e) (car s) (cadr s)). Now you should try (postfix '(1 2 pair?))

    I've made many interpreters and none of them used eval. Here is what I would have done most of the time:

    ;; Usually you know what operators are supported
    ;; so you can map their symbol with a procedure
    (define (symbol->proc sym)
      (case sym
        [(+) +]
        [(hyp) (lambda (k1 k2) (sqrt (+ (* k1 k1) (* k2 k2))))]
        [else (error "No such operation" sym)]))
    

    This fixes the (postfix '(1 2 pair?)) problem. A thing that I see in your code is that you always assume two arguments. But how would you do a double? eg something that just doubles the one argument. In this case symbol->proc could return more information:

    (define (symbol->op sym)
      (case sym
        [(+) (cons + 2)]
        [(double) (cons (lambda (v) (* v v)) 1)]
        [else (error "No such operation" sym)]))
    
    (define op-proc car)
    (define op-arity cdr)
    

    And in your code you could do this if it's not a number:

    (let* ([op (symbol->op e)]
           [proc (op-proc op)]
           [arity (op-arity op)])
      (cons (apply proc (take s arity)
            (drop s arity)))
    

    take and drop are not R5RS, but they are simple to create.