I am a beginner to Python. I have tried learning python and C++ in the past had learnt about like classes and stuff but then had to abandon it for reasons, now I am learning python from the beginning as I have forgotten it all.
So I was trying to make a calculator like the ones you have in mobile using python but without any GUI. Now, the problem I am having right now is, see in your mobile calculator you can do one operation after the other i.e. say you typed 95+8x2, that mobile calculator will have no problem in deducing operator precedence from your input and give result as 111 and I am trying to do something similar.
But the problem is, the way I know how to do it as a beginner would require a lot of code, it would not be complex but get too long and hence a lot of time wasted. Here is how I have thought of doing it right now :
Find the location of each of the operators in the input for that I am using their indexes i.e. I have named the input as 'alg_operation' and for example, I am using alg_operation.find('*') to where the multiplaction operator is, I am calling this index as location_prod using this I am able to calculate the product simply via converting the part of the string that comes before the operator into float then multiply it with the other part that comes just after (obviously converting it into float as well).
After finding the location of each of the 5 operators that I have decided to include i.e. exponentiation, multiplication, division (// not /), addition and subtraction, I would have to write code for 120 different cases of the arrangement of these operators, which may not be complex but definitely will take a lot of time.
How can I quickly deduce operator precedence from the string input ?
I will update this post if I learn anything new, since I am a beginner to programming.
You can indeed evaluate an arbitrary python expression with eval. Use of eval is a pretty big code smell, because it lets you execute anything, but for completeness it would be done like this:
expr = input("Please, please, please only write maths: ")
print(eval(exp))
note that you could type import shutil; shutil.rmtree("/home") and python would cheerfully run it. So obviously we don't want to do this.
We could try to protect ourselves by sanitising the input. In this case this might actually work, with something like:
safe_chars = (".", "*", "+", "/", "-"," ", *range(10))
if not all(x in safe_chars for x in expr):
raise ValueError("You tried to enter dangerous data!")
I can't immediately think of any way to do anything dangerous with input consisting only of those chars, but doubtless someone will point it out immediately in the comments [in which case I'll add it here]. More generally, sanitising data like this is hard, because in order to know what's safe you really need to understand the input, by which point you've just written a parser.
Please do note that eval is inherently dangerous. It can be a useful hack for once-off code, although even then... and it is of course useful when you actually want to evaluate python code.
Converting it to reverse polish notation will solve your problem
def readNumber(string: str,index: int):
number = ""
while index < len(string) and isNumber(string[index]):
number += string[index]
index += 1
return float(number), index
def isOperator(token):
operators = ['+', '-', '*', '/', '%']
return token in operators
def isNumber(token):
return (token >= '0' and token <= '9') or token == "."
def toRpn(string: str):
"""
Converts infix notation to reverse polish notation
"""
precedence = {
'(': 0,
'-': 1,
'+': 1,
'*': 2,
'/': 2,
'%': 2,
}
i = 0
fin = []
ops = []
while i < len(string):
token = string[i]
if isNumber(token):
number, i = readNumber(string,i)
fin.append(number)
continue
if isOperator(token):
top = ops[-1] if ops else None
if top is not None and precedence[top] >= precedence[token]:
fin.append(ops.pop())
ops.append(token)
i += 1
continue
if token == '(':
ops.append(token)
i += 1
continue
if token == ')':
while True:
operator = ops.pop()
if operator == '(':
break
fin.append(operator)
if not ops:
break
i += 1
continue
i += 1
while ops:
fin.append(ops.pop())
return fin
def calculate_rpn(rpn: list):
"""
Calculates the result of an expression in reverse polish notation
"""
stack = []
for token in rpn:
if isOperator(token):
a = stack.pop()
b = stack.pop()
if token == '+':
stack.append(b + a)
elif token == '-':
stack.append(b - a)
elif token == '*':
stack.append(b * a)
elif token == '/':
stack.append(b / a)
elif token == '%':
stack.append(b % a)
else:
stack.append(token)
return stack[0]
print ("90165: ", calculate_rpn(toRpn("27+38+81+48*33*53+91*53+82*14+96")))
print ("616222: ", calculate_rpn(toRpn("22*26*53+66*8+7*76*25*44+78+100")))
print (calculate_rpn(toRpn("(22+4)*4")))
My Github
You can easily add more operators and their precedence if you want.
You have to modify the precedence array and the isOperator function. Also you should modify the function of the respective operator in the calculate_rpn function.
Related
I am creating a program that takes in a boolean expression as a string and converts the infix formula to postfix, while making sure the formula is in a valid form. What I'm having a hard time doing is figuring out a way to check if the inputed formula is valid or not. NO IMPORTING is permitted (use built-in python functions/methods), loops and recursion are allowed. If the formula is invalid, return None.
The formula can contain:
variables in 'abcdefghijklmnopqrstuvwxyz'
operators in '-+*'
where - is NOT, + is OR, * is AND
Here are some valid formulas (as Python strings).
"x"
"-y"
"(x*y)"
"((-x+y)*(-y+x))"
Here are some strings that are not formulas.
"X" variable not lower case letter
"x*y" missing parentheses
"-(x)" extraneous parentheses
"(x+(y)*z)" mismatched parentheses
Some conversion examples are:
(x+y) -> xy+
(x*y) -> xy*
-(x+y) -> xy+-
-x -> x-
((x*y)+(z*x)) -> xy*zx*+
A full working program isn't necessary, an algorithm to check if the formula is valid or not is fine.
My current implementation to convert formula from infix to postfix:
def infix_to_postfix(infix_expression):
precedence = {}
precedence["*"] = 2
precedence["+"] = 2
precedence["-"] = 2
precedence["("] = 1
storage_stack = Stack()
postfix_list = []
tokenList = list(infix_expression)
for token in tokenList:
if(token not in "-+*()"):
postfix_list.append(token)
elif(token == '-'):
storage_stack.push(token)
elif(token == '('):
storage_stack.push(token)
elif(token == ')'):
topToken = storage_stack.pop()
while(topToken != '('):
postfix_list.append(topToken)
topToken = storage_stack.pop()
else:
while(not storage_stack.is_empty() and precedence[storage_stack.peek()] >= precedence[token]):
postfix_list.append(storage_stack.pop())
storage_stack.push(token)
while(not storage_stack.is_empty()):
postfix_list.append(storage_stack.pop())
result = "".join(postfix_list)
return result
I need to find a way to check that the formula is valid while changing the positions of the operators and variables.
Edit:
I've come up with part of an algorithm to check if the formula is valid or not:
((a+b)*(c+d))
F1 = (a+b)
F2 = (c+d)
((a+b)*(c+d)) = (F1 * F2)
If F1 and F2 are valid, then the whole thing is valid.
A formula enclosed with parenthesis is valid if:
There is ( and ), there is either a + or * in between 2 sub-formulas, and both sub-formulas are valid.
I have this idea of checking, but I have no idea how to implement it. Most likely recursion.
This is an old question, however here is a potential solution to someone having the same issue.
import re
def formula(string):
if not string: return 0
string = re.sub("[^0-9+*/%-=]", "", string)
try:
string, result = string.split("=")
return eval(string) == int(result)
except:
return 0
Recently, I got an interview question which says to convert string expressions like "1+2-3" and "-2+4" to 0 and 2 respectively. Assuming the inputs are single digits numbers followed by signs and no NULL input. I tried this output but the interviewer said I am close but not perfect solution. Please help me here. Thanks.
def ans(input):
result, j = 0, 0
for i in input:
if i == '+' or i == '-':
j = i
else:
i = int(i)
result = result j i
return result
ans("1+2-3")
ans("-2+4")
I am making some silly mistake but I am learning. Thanks in advance.
Two things need fixing to work at all:
You need to handle the initial value properly; when the initial value is non-negative, this fails. Before the loop, set j = '+' so a non-sign prefixed value is added (also, for style points, j is a terrible name, could you use op or something?).
You can't use variables as operators.
Replace:
result = result j i
with:
if j == '+':
result += i
else:
result -= i
Note: If modules are allowed, a generalization can be used to handle operators the "nice" way (though more work would be needed to obey operator precedence). You'd define:
import operator
ops = {'+': operator.add, '-': operator.sub, ...}
then make the initial value of op operator.add and change the test for operators to:
if i in ops:
op = ops[i]
else:
result = op(result, int(i))
which scales to many more operators, dynamically selecting the operation to perform without cascading if/elif checks.
Side-note: While violating the spirit of the challenge, ast.literal_eval (at least as of Python 3.5, and this may change, see bug #22525) will actually safely parse strings like this (eval is unsafe, since it can execute arbitrary code, but ast.literal_eval can only parse Python literals and apparently some basic compile-time math). So you could just do:
import ast
ans = ast.literal_eval
Sure, it handles many other literals too, but we never defined the failure case behavior anyway. :-)
Using eval() is the simplest solution. Like
eval("1+2-3")
The following code give another solution without using built-in eval
import operator
class Parse(object):
def __init__(self, input):
self.input = input
self.pos = 0
self.end = len(input)
def eval(self):
result = self.match_digits()
while self.pos < self.end:
op = self.match_operator()
operand = self.match_digits()
result = op(result, operand)
return result
def match_operator(self):
look_ahead = self.input[self.pos]
self.advance()
return operator.add if look_ahead == '+' else operator.sub
def match_digits(self):
look_ahead = self.input[self.pos]
positive = 1
if look_ahead == '-':
positive = -1
self.advance()
digits, s = 0, self.pos
while s < self.end and self.input[s].isdigit():
digits = digits * 10 + int(self.input[s])
s += 1
self.advance(s-self.pos)
return digits * positive
def advance(self, offset=1):
self.pos += offset
For testing
p = Parse(input='2+1+0-3')
print p.eval()
p = Parse(input='-2+-13+3')
print p.eval()
I think the most flexible solution (not using eval and able to handle any operations) is to parse the string into a binary (red-black) tree, where leafs are numbers and branches operators (+,-,/,*,etc).
For example, "1+(5*12)/17" would be parsed into following structure:
"+"
/ \
1 "/"
/ \
"()" 17
/
"*"
/ \
5 12
Once you've parsed a string into this structure, it's easy to compute by traversing branches depth-first, right to left.
If you need to handle variables, then you'd have to get locals() and replace accordingly, either as you parse the string, or as you traverse the tree.
EDIT:
I created a working example to illustrate this, you can find the source on github: https://github.com/MJWunderlich/py-math-expression-evaluator
what about:
def f(s):
s = s.strip()
for i, c in enumerate(s):
if c == '+':
return f(s[:i]) + f(s[i+1:])
if c == '-':
return f(s[:i]) - f(s[i+1:])
for i, c in enumerate(s):
if c == '*':
return f(s[:i]) * f(s[i+1:])
if c == '/':
return f(s[:i]) / f(s[i+1:])
return 0 if s == '' else int(s)
? Doesn't work with parenthesis
This question already has answers here:
Evaluating a mathematical expression in a string
(14 answers)
Closed 8 years ago.
I've got a python formula that randomly places operands in between numbers. The list could, for example, look like this:
['9-8+7', '7-8-6']
What I want to do is get the value of each string, so that looping through the strings, an array would see 9-8+7 and would append 8 and 7-8-6 would append -7. I can't convert a string with operands to int, so is this possible at all? Or should I change the algorithm so that instead of creating a string with each random output it calculates the value of it immediately?
Thank you in advance.
You can do eval on the list items, but that's a potential security hole and should only be used if you fully trust the source.
>>> map(eval, ['9-8+7', '7-8-6'])
[8, -7]
If you control the code producing the string, computing the values directly sounds like a better approach (safer and probably faster).
As Fredrik pointed out, you can do eval in Python. I thought I'd add a more general approach that would work in any language, and might shed some light on simple parsers for those that haven't seen them in action.
You're describing a language whose formal definition looks something like this:
expr := sum
sum := prod [("+" | "-") prod]...
prod := digit [("*" | "/") digit]...
digit := '0'..'9'
This grammar (which I'm not bothering to make correct EBNF) accepts these strings: "3", "4*5/2", and "8*3+9", and so on.
This gives us a clue how to parse it, and evaluation is no more work than accumulating results as we go. The following is working Python 2 code. Notice how closely the code follows the grammar.
class ParseFail(Exception):
pass
def eval_expr(str):
value, pos = eval_sum(str, 0)
return value
def eval_sum(str, pos):
value, pos = eval_product(str, pos)
accum = value
while pos != len(str):
op = str[pos]
if not str[pos] in ['+', '-']:
raise ParseFail("Unexpected symbol at position "
"{pos} of {str}".format(str=str, pos=pos))
value, pos = eval_product(str, pos + 1)
if op == '+':
accum += value
else:
accum -= value
return accum, pos
def eval_product(str, pos):
value, pos = eval_digit(str, pos)
accum = value
while pos != len(str):
op = str[pos]
if not str[pos] in ['*', '/']:
return accum, pos
value, pos = eval_digit(str, pos + 1)
if op == '*':
accum *= value
else:
accum /= value
return accum, pos
def eval_digit(str, pos):
if not str[pos].isdigit():
raise ParseFail("Unexpected symbol at position "
"{pos} of {str}".format(str=str, pos=pos))
return int(str[pos]), pos + 1
try:
print "3 ->", eval_expr("3")
print "3*4 ->", eval_expr("3*4")
print "2+3*4-5 ->", eval_expr("2+3*4-5")
# Should raise ParseFail
print "2+3*4^2-5 ->", eval_expr("2+3*4^2-5")
except ParseFail as err:
print
print err.args[0]
Here's a sample run:
$ python simple_expr.py
3 -> 3
3*4 -> 12
2+3*4-5 -> 9
2+3*4^2-5 ->
Unexpected symbol at position 5 of 2+3*4^2-5
It would be pretty easy to extend this to a full string calculator with more operators, such as the exponent operator '^' and multi-digit integers. Parentheses, floats and functions might be a bit of work, but not that hard either. Every programmer should try it once in their lives, in my opinion.
This of course depends on how well-behaved and restricted your expressions are.
Since subtraction is addition with a negative number, you can write the subtractions as additions with a negative number. Spit on + to find the terms. Then parse the terms of the sum to integers, and sum them. Do so for each expression.
[sum(map(int,l.replace('-', '+-').split('+'))) for l in ['9-8+7','7-8-6']]
I have next algorithm for parsing expressions in Python:
def parse(strinput):
for operator in ["+-", "*/"]:
depth = 0
for p in range(len(strinput) - 1, -1, -1):
if strinput[p] == ')': depth += 1
elif strinput[p] == '(': depth -= 1
elif depth==0 and strinput[p] in operator:
# strinput is a compound expression
return (strinput[p], parse(strinput[:p]), parse(strinput[p+1:]))
strinput = strinput.strip()
if strinput[0] == '(':
# strinput is a parenthesized expression?
return parse(strinput[1:-1])
# strinput is an atom!
return strinput
(it can be found here: http://news.ycombinator.com/item?id=284842)
I have hard time understanding it, since I don't find Python docs very helpful for this situation. Can someone tell me what line: for operator in ["+-", "*/"]:means?
I know it's structure like for each string variable which is operator in array of this 2 elements, but why is it written like this ["+-, */"]? How does Python separate it? In first iteration, operator is "+-"?
Any help would mean a lot. Thanks
You're correct; for operator in ["+-", "*/"]: means operator will be "+-" the first time through and "*/" the second time through the loop.
Notice how later it checks if strinput[p] in operator. Python treats a string as a list of characters, so this expression will only be true if strinput[p] is equal to "+" or "-" on the first time through and "*" or "/" the second time through.
(The reason they do this is for order of operations- "+" and "-" get equal but lower precedence to "*" and "/")
I am new to programming, and I am trying to write a Vigenère Encryption Cipher using python. The idea is very simple and so is my function, however in this line:
( if((BinKey[i] == 'b')or(BinKey[i+1] == 'b')): )
It seems that I have an index problem, and I can't figure out how to fix it.
The error message is:
IndexError: string index out of range
I tried to replace the i+1 index by another variable equal to i+1, since I thought that maybe python is re-incrementing the i, but it still won't work.
So my questions are:
How to fix the problem, and what have I done wrong?
Looking at my code, what can I learn to improve my programming skills?
I want to build a simple interface to my program (which will contain all the encryption ciphers), and all I came up with from Google is pyqt, but it just seems too much work for a very simple interface, so is there a simpler way to build an interface? (I am working with Eclipse Indigo and pydev with Python3.x)
The Vigenère Encryption function (which contains the line that causes the problem) is:
def Viegner_Encyption_Cipher(Key,String):
EncryptedMessage = ""
i = 0
j = 0
BinKey = Bin_It(Key)
BinString = Bin_It(String)
BinKeyLengh = len(BinKey)
BinStringLengh = len(BinString)
while ((BinKeyLengh > i) and (BinStringLengh > j)):
if((BinKey[i] == 'b')or(BinKey[i+1] == 'b')):
EncryptedMessage = EncryptedMessage + BinKey[i]
else:
EncryptedMessage = EncryptedMessage + Xor(BinKey[i],BinString[j])
i = i + 1
j = j + 1
if (i == BinKeyLengh):
i = i+j
return EncryptedMessage
This is the Bin_It function:
def Bin_It(String):
TheBin = ""
for Charactere in String:
TheBin = TheBin + bin(ord(Charactere))
return TheBin
And finally this is the Xor function:
def Xor(a,b):
xor = (int(a) and not int(b)) or (not int(a) and int(b))
if xor:
return chr(1)
else:
return chr(0)
In your while condition, you are ensuring that i < len(BinKey). This means that BinKey[i] will be valid, but BinKey[i+1] will not be valid at the last iteration of your loop, as you will then be accessing BinKey[len(BinKey)], which is one past the end of your string. Strings in python start at 0 and end at len-1 inclusive.
To avoid this, you can update your loop criterion to be
while BinKeyLength > i+1 and ...:
You can either change
while ((BinKeyLengh > i) and (BinStringLengh > j)):
to
while ((BinKeyLengh > i-1) and (BinStringLengh > j)):
or change
if((BinKey[i] == 'b')or(BinKey[i+1] == 'b')):
to
if((BinKey[i] == 'b') or (BinKeyLengh > i-1 and BinKey[i+1] == 'b')):
This will avoid trying to go into BinKey[BinKeyLength], which is out of scope.
Looking at my code, what can I learn to improve my programming skills?
Looping over an index is not idiomatic Python. It's better to loop over the elements of an iterator where possible. After all, that's usually what you're interested in: for i in... is often followed by my_list[i].
In this example, you should use the built-in function zip (or itertools.izip if your code is lazy, though this isn't necessary in Python 3), which gives you pairs of values from two or more iterators, and stops when the shortest one is exhausted.
for key_char, string_char in zip(BinKey, BinString): # takes values sequentially from
# BinKey and BinString
# and binds them to the names
# key_char and string_char
# do processing on key_char and string_char
If you really must do a while loop on an index, then put the test the other way around so it's clearer what you're doing. Compare
while len(BinKey) > i and len(BinString) > j: # this looks like len(BinKey) and
# len(BinString) are varying and you're
# comparing them to static variables i and j
with
while i < len(BinKey) and j < len(BinString): # this looks like you're varying i and j
# and comparing them to len(BinKey) and len(BinString)
Which better communicates the purpose of the loop?
Finally, the clause
if (i == BinKeyLengh):
i = i+j
doesn't seem to do anything. If i == BinKeyLength then the while loop will stop in a moment anyway.
I think your error, as Python interpreter says, is that you accessing invalid array positions.
In order to solve this, unlike it's being said, you should change your code to
while (BinKeyLength > i+2 and ...):
This is because in the last step, BinKeyLength = i+2, then i+1 is BinKeyLength-1, which is the last position of your array.
Concerning your programming skills I recommend you two things:
Be the code. Sounds mystic but the most important thing that is missing here is figuring out which indices numbers are used.
As it has been said by rubik, follow some style guides like PEP8 style guide.