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)
-
\$\begingroup\$ (Welcome to CR!) (Do you know Rosetta?) \$\endgroup\$greybeard– greybeard2018年02月18日 18:59:40 +00:00Commented Feb 18, 2018 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– WorBlux2018年05月28日 15:53:39 +00:00Commented May 28, 2018 at 15:53
1 Answer 1
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))))]))
Explore related questions
See similar questions with these tags.