1
\$\begingroup\$

I have implemented a stack calculator based on this programming task. I wondered if a more experienced racketeer could give me feedback and tell me if I am missing anything in Racket that would enable the solution to be more elegant.

#lang racket

(define (inc n)
  (+ n 1))

(define (print-value s)
  (begin
    (display s)
    (display " ")))

(define (exec l (stack empty) (opcount 1))
  "executes a stack based program."
  (cond
    [(empty? l) (void)]

    ;; push number onto stack
    [(number? (string->number (first l)))
     (exec (rest l)
           (cons (string->number (first l)) stack)
                 (inc opcount))]

    ;; pop number from stack
    [(equal? (first l) ".")
     (begin
       (print-value (first stack))
       (exec (rest l) (rest stack)
       (inc opcount)))]

    ;; mathmatical operators
    [(equal? (first l) "+")
     (exec (rest l) (cons
                     (+ (first stack) (second stack))
                     (rest (rest stack))) (inc opcount))]
    [(equal? (first l) "-")
     (exec (rest l) (cons
                     (- (first stack) (second stack))
                     (rest (rest stack))) (inc opcount))]
    [(equal? (first l) "*")
     (exec (rest l) (cons
                     (* (first stack) (second stack))
                     (rest (rest stack))) (inc opcount))]
    [(equal? (first l) "/")
     (exec (rest l) (cons
                     (/ (first stack) (second stack))
                     (rest (rest stack))) (inc opcount))]

    ;; duplication operator
    [(equal? (string-downcase (first l)) "dup")
     (exec (rest l)
           (cons (first stack) stack) (inc opcount))]
    [else
     (~a "Error: operation " opcount " invalid")]))

(define program (string-split "64 DUP * ."))

(exec program)
\$\endgroup\$
  • \$\begingroup\$ (Welcome to CR!) (Do you know Rosetta?) \$\endgroup\$ – greybeard Feb 18 '18 at 18:59
  • \$\begingroup\$ Cond clauses have an implicit begin, and support any arbitrary number us secondary clauses, executed in the order they appear. This the begin in the "." operator case is unneeded. \$\endgroup\$ – WorBlux May 28 '18 at 15:53
1
\$\begingroup\$

Bug

The - and / operators interpret their operands backwards from the typical RPN order. That is, I expect "10 7 -" should produce 3, but it actually produces -3.

Recursion

Your code is awfully repetitive, because the handler for each operator has to fetch the operator ((first l)), perform the operation, then make the recursive call ((exec (rest l) … (inc opcount))). It would be worthwhile to define a helper function that applies the operator to the stack.

(define (apply-op op stack)
  (cond
    [(number? (string->number op))
     (cons (string->number op) stack)]
    [(equal? op ".")
     (begin
       (display (first stack))
       (display " ")
       (rest stack))]
    [(equal? op "+")
     (cons (+ (second stack) (first stack)) (rest (rest stack)))]
    [(equal? op "-")
     (cons (- (second stack) (first stack)) (rest (rest stack)))]
    [(equal? op "*")
     (cons (* (second stack) (first stack)) (rest (rest stack)))]
    [(equal? op "/")
     (cons (/ (second stack) (first stack)) (rest (rest stack)))]
    [(equal? (string-downcase op) "dup")
     (cons (first stack) stack)]
    [else
     (~a "operation '" op "' invalid")]))

Then, the exec function just has to call it and drive the recursion.

(define (exec ops (stack empty) (opcount 1))
  "executes a stack based program."
  (cond
    [(empty? ops) (void)]
    [else
      (let ([next-stack (apply-op (first ops) stack)])
           (if (string? next-stack)
               (~a "Error at operator " opcount ": " next-stack)
               (exec (rest ops) next-stack (+ opcount 1))))]))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.