chevron_left chevron_right
Login Register invert_colors photo_library Stay updated and chat with others! - Join the Discord!
Thread Rating:
• 0 Vote(s) - 0 Average

 Tutorial How My CLI Calculator Works filter_list Linear Mode Threaded Mode View a Printable Version Author Message
How My CLI Calculator Works #1
Ok, so I recently posted the code for my CLI Calculator in a challenge. It was requested of me to explain the code, so here is an explanation of why it works. If you don't understand something, just ask.

This is the code:
Code:
```import re def lex(equation):     # remove all the whitespaces from the equation string     equation = equation.replace(" ", "")     return re.findall("[0-9]+|[\+, \-, \*, \/]", equation) def evalMulDiv(tokens):     i = 0     while i < len(tokens):         if tokens[i] == "*":             tokens = tokens[:i - 1] + [int(tokens[i - 1]) * int(tokens[i + 1])] + tokens[i + 2:]             i -= 2         elif tokens[i] == "/":             tokens = tokens[:i - 1] + [int(tokens[i - 1]) / int(tokens[i + 1])] + tokens[i + 2:]             i -= 2         i += 1     return tokens def evalAddSub(tokens):     i = 0     while i < len(tokens):         if tokens[i] == "+":             tokens = tokens[:i - 1] + [int(tokens[i - 1]) + int(tokens[i + 1])] + tokens[i + 2:]             i -= 2         elif tokens[i] == "-":             tokens = tokens[:i - 1] + [int(tokens[i - 1]) - int(tokens[i + 1])] + tokens[i + 2:]             i -= 2         i += 1     return tokens def calc(equation):     tokens = lex(equation)     tokens = evalMulDiv(tokens)     return int(evalAddSub(tokens)) equation = raw_input("> ") print(calc(equation))```

There are four function in our code: lex, evalMulDiv, evalAddSub, calc.
The function lex just takes our input string and splits up the operands and operators into an array.
Our calc function is just for organization, it calls all the needed functions to calculate the value of our equation.
There are two separate eval functions because of operator precedence. Operator precedence is just the math rule that you must calculate the multiplication and division of a problem before the addition and subtraction.
You don't have to fully understand the eval functions, but here's how they work. First, in the evalMulDiv function, we loop through all the tokens in our equation... if we find either the * or / operator, we will do that specific math (* or /) on the two operands before and after us. The same is true for our evalAddSub function, except we find the + and - operators.
So, this is how it all works together:
Code:
```lex("2+ 3*50") -> ["2", "+", "3", "*", "50"] evalMulDiv(["2", "+", "3", "*", "50"]) -> ["2", "+", "150"] evalAddSub(["2", "+", "150"]) -> ["152"]```

RE: How My CLI Calculator Works #2
Thanks m0dem! Is tokens a default variable in Python or am I just missing something in the code?

Could you just further explain what exactly this returns?
Code:
`return re.findall("[0-9]+|[\+, \-, \*, \/]", equation)`

Can you also explain the logic behind these further?
Code:
```if tokens[i] == "*":             tokens = tokens[:i - 1] + [int(tokens[i - 1]) * int(tokens[i + 1])] + tokens[i + 2:]             i -= 2```

I'm assuming you're just trying to find a multiplication symbol in the equation, and then just multiply the number that comes before and after it.
But wouldn't these be the only two you need: [int(tokens[i - 1]) * int(tokens[i + 1])]. I want to know the significance of the rest of that code.

RE: How My CLI Calculator Works #3
(12-15-2015, 06:15 PM)God Wrote: Thanks m0dem! Is tokens a default variable in Python or am I just missing something in the code?

Could you just further explain what exactly this returns?
Code:
`return re.findall("[0-9]+|[\+, \-, \*, \/]", equation)`

Can you also explain the logic behind these further?
Code:
```if tokens[i] == "*":             tokens = tokens[:i - 1] + [int(tokens[i - 1]) * int(tokens[i + 1])] + tokens[i + 2:]             i -= 2```

I'm assuming you're just trying to find a multiplication symbol in the equation, and then just multiply the number that comes before and after it.
But wouldn't these be the only two you need: [int(tokens[i - 1]) * int(tokens[i + 1])]. I want to know the significance of the rest of that code.

A token is not a default variable, it is just a piece of data that you need to split your whole string up into.
That regular expression just splits the string at any number or operator (*, /, +, -) without removing that value. It's a bit hard to understand if you don't know RE.

The logic is, you have to put the new value into the tokens list correctly. Just can't just make a new list with that single number. You have to remove all 3 tokens that used to be there and insert in the one new calculated value.

I hope that make sense. RE: How My CLI Calculator Works #4
(12-15-2015, 07:45 PM)m0dem Wrote: A token is not a default variable, it is just a piece of data that you need to split your whole string up into.
That regular expression just splits the string at any number or operator (*, /, +, -) without removing that value. It's a bit hard to understand if you don't know RE.

The logic is, you have to put the new value into the tokens list correctly. Just can't just make a new list with that single number. You have to remove all 3 tokens that used to be there and insert in the one new calculated value.

I hope that make sense. Okay, thanks. I guess you recommend reading into the re library more. I'm sure it'll help me understand the logic even more and how everything is being split up and stored.

So just to clarify, basically tokens is the variable for whatever "return re.findall("[0-9]+|[\+, \-, \*, \/]", equation)" returns? And then you're passing it into the eval functions to do whatever you need with it?

RE: How My CLI Calculator Works #5
(12-15-2015, 08:03 PM)God Wrote: Okay, thanks. I guess you recommend reading into the re library more. I'm sure it'll help me understand the logic even more and how everything is being split up and stored.

So just to clarify, basically tokens is the variable for whatever "return re.findall("[0-9]+|[\+, \-, \*, \/]", equation)" returns? And then you're passing it into the eval functions to do whatever you need with it?

Our tokens variable is just the list of characters (e.g. ["5", "+", "8", "*", "10"]) our regular expression returns. And then I pass those tokens to the eval's.
btw: I'm not a huge fan of regular expressions.

RE: How My CLI Calculator Works #6
(12-15-2015, 08:22 PM)m0dem Wrote: Our tokens variable is just the list of characters (e.g. ["5", "+", "8", "*", "10"]) our regular expression returns. And then I pass those tokens to the eval's.
btw: I'm not a huge fan of regular expressions.

Okay makes sense. Thanks for the explanation.

Do you have a Skype?

RE: How My CLI Calculator Works #7
Nice work. I made a oneline calculator challenge on another forum thinking my ruby answer was hot shit, then someone reminded me that eval() existed Anyway, you could change your lex function to the below code, because the findall skips over anything that isn't an integer, period, or operator (which would support float input with some tweaking):
Code:
```def lex(equation):     #\d is the same as [0-9]     return re.findall("\d+|\.|[\+, \-, \*, \/]",equation)```

RE: How My CLI Calculator Works #8
(12-16-2015, 04:40 AM)Nevermore Wrote: Nice work. I made a oneline calculator challenge on another forum thinking my ruby answer was hot shit, then someone reminded me that eval() existed Anyway, you could change your lex function to the below code, because the findall skips over anything that isn't an integer, period, or operator (which would support float input with some tweaking):
Code:
```def lex(equation):     #\d is the same as [0-9]     return re.findall("\d+|\.|[\+, \-, \*, \/]",equation)```

Hey @Nevermore! Good to see you back. That person who reminded you about eval() was wrong. Eval() can be a big security risk, and it is just not good practice. (kind of like os.system())
Plus a lot can be learned by making your own parsers. This stuff is very applicable to making programming languages! (really cool stuff)
Thanks for the suggestions! As you can probably tell, I'm not great with RE yet. (haven't used it much)

Users browsing this thread: 1 Guest(s)