Are you reading htdp?
You should try using the design recipe to write this program. Right now, your two functions are each performing several tasks at once and it's hard to tell what's going on. Following the design recipe and other guidelines that htdp recommends will make this program clearer, more organized, and easier to work with.
At the very least, you should add purpose statements and signatures to your functions so what they are doing is more clear. Also, it would be very beneficial to choose more descriptive variable names. What is h
?
I highly recommend htdp for generally learning how to systematically design programs and solve problems.
If you really want to make a calculator that takes in a string and evaluates it, I think you should wait until you're using advanced student language. You'll have learned a lot more tricks by then and you'll be able to do this much more cleanly. Also, don't try to use the full racket
language yet. There are a lot of things in there that are confusing and will distract you from what's most important right now. There are things that are too powerful that you'll end up mis-using and forming bad habits with.
For now, I'd drop lexing and just focus on calc
. I'd also recommend using prefix arithmetic like racket, rather than infix arithmetic like you'd write in other languages or by hand. Trying to parse infix arithmetic is a huge headache and in my opinion is not the interesting part of a calculator. If you really want infix, I'd recommend forcing everything to have parentheses around it.
Right now, your calculator is doing the order of operations incorrectly. For example, (calc (lex "9-4-3"))
should be 2, but your calculator returns 8 because your calculator is right-associative. This means it's like your calculator interprets '(9 - 4 - 3)
as 9 - (4 - 3)
when it should be (9 - 4) - 3
. Like I said, parsing infix arithmetic is tricky. That's why I recommend forcing everything to have parentheses around it, so it'd be '((9 - 4) - 3)
or if you did prefix arithmetic, '(- (- 9 4) 3)
.
If you want to add multiplication and other operators into the mix, this'll get even messier. You'll have to make sure your calculator interprets '(1 + 2 * 3 + 4)
as (1 + (2 * 3)) + 4
, which would be a pain. If you really want to do that, you should add a parsing step before calc
that converts an s-expression into a custom data type and then calc
should take that in. For example, you'd have an add
struct that has two fields, one for each operand. You'd also have a similar sub
struct for subtraction.
Lexing involves converting a string into a list of tokens. In this case, a token is a number or a symbol for an operator. Parsing involves converting a list of tokens into some structured representation, like those custom data types I was talking about. Evaluation involves taking that structured representation and reducing it to a single, simple value. In this case, a number. Right now, you have parsing implicitly happening in calc
, which also evaluates. If you want to add more operators, your arithmetic language will get complicated enough where it'll be worth having a parser. However, if you just require everything to be parenthesized, a parser isn't really necessary and it'll be easy to add something like multiplication to your calculator.