Calculate from list of strings - Python [duplicate] - python

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']]

Related

How do I detect any of 4 characters in a string and then return their index?

I would understand how to do this assuming that I was only looking for one specific character, but in this instance I am looking for any of the 4 operators, '+', '-', '*', '/'. The function returns -1 if there is no operator in the passed string, txt, otherwise it returns the position of the leftmost operator. So I'm thinking find() would be optimal here.
What I have so far:
def findNextOpr(txt):
# txt must be a nonempty string.
if len(txt) <= 0 or not isinstance(txt, str):
print("type error: findNextOpr")
return "type error: findNextOpr"
if '+' in txt:
return txt.find('+')
elif '-' in txt:
return txt.find('-')
else
return -1
I think if I did what I did for the '+' and '-' operators for the other operators, it wouldn't work for multiple instances of that operator in one expression. Can a loop be incorporated here?
Your current approach is not very efficient, as you will iterate over txt, multiple times, 2 (in and find()) for each operator.
You could use index() instead of find() and just ignore the ValueError exception , e.g.:
def findNextOpr(txt):
for o in '+-*/':
try:
return txt.index(o)
except ValueError:
pass
return -1
You can do this in a single (perhaps more readable) pass by enumerate()ing the txt and return if you find the character, e.g.:
def findNextOpr(txt):
for i, c in enumerate(txt):
if c in '+-*/':
return i
return -1
Note: if you wanted all of the operators you could change the return to yield, and then just iterate over the generator, e.g.:
def findNextOpr(txt):
for i, c in enumerate(txt):
if c in '+-*/':
yield i
In []:
for op in findNextOpr('1+2-3+4'):
print(op)
Out[]:
1
3
5
You can improve your code a bit because you keep looking at the string a lot of times. '+' in txt actually searches through the string just like txt.find('+') does. So you can combine those easily to avoid having to search through it twice:
pos = txt.find('+')
if pos >= 0:
return pos
But this still leaves you with the problem that this will return for the first operator you are looking for if that operator is contained anywhere within the string. So you don’t actually get the first position any of these operators is within the string.
So what you want to do is look for all operators separately, and then return the lowest non-negative number since that’s the first occurence of any of the operators within the string:
plusPos = txt.find('+')
minusPos = txt.find('-')
multPos = txt.find('*')
divPos = txt.find('/')
return min(pos for pos in (plusPos, minusPos, multPos, divPos) if pos >= 0)
First, you shouldn't be printing or returning error messages; you should be raising exceptions. TypeError and ValueError would be appropriate here. (A string that isn't long enough is the latter, not the former.)
Second, you can simply find the the positions of all the operators in the string using a list comprehension, exclude results of -1, and return the lowest of the positions using min().
def findNextOpr(text, start=0):
ops = "+-/*"
assert isinstance(text, str), "text must be a string"
# "text must not be empty" isn't strictly true:
# you'll get a perfectly sensible result for an empty string
assert text, "text must not be empty"
op_idxs = [pos for pos in (text.find(op, start) for op in ops) if pos > -1]
return min(op_idxs) if op_idxs else -1
I've added a start argument that can be used to find the next operator: simply pass in the index of the last-found operator, plus 1.

Checking if Boolean Mathematical Formula is in the Proper Form

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

Covert a string expression to numerical value in python

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

How do I check if each digit of a number is less than 2? (python) [duplicate]

This question already has answers here:
How to check if a input is in binary format(1 and 0)?
(8 answers)
Closed 7 years ago.
I'm trying to create a program that checks if each digit of a given number is less than 2, possibly using
range(len(a)):
def is_bin(number):
try:
int(str(number), 2)
except ValueError:
return False
return True
You can convert number to string and check like this:
all(int(c) < 2 for c in str(n))
For example:
>>> all(int(c) < 2 for c in str(1011))
True
>>> all(int(c) < 2 for c in str(1211))
False
You can try this
num = 123457
>>>all(int(i)<2 for i in str(num))
False
num = 11011
>>>all(int(i)<2 for i in str(num))
True
Python is dynamically typed, so this is relatively easy to do. I would suggest converting your integer into a string, then iterating through the string and checking each digit.
Using that method, the code would probably look something like:
your_number_string = str(your_number)
for d in range(0, len(your_number_string)):
i = your_number_string[d]
if (int(i) > 2):
raise new Exception("Digit " + str(i) + "is not less than 2")
Couple things to note with this: It's bad practice to throw bare Exceptions. If you like the exception route, then extend the exception class and make your own. This also assumes that your number is a valid integer. Finally, this also will only alert you for the first digit larger than two, it won't tell you about any subsequent ones that also are larger than 2. This will require some adjustments for negative numbers and floats.
Could use the good ol' fashioned method :) just take your number and extract each digit and then update your number.
while yournumber != 0 :
digit = yournumber % 10
yournumber = younumber / 10
if digit < 2
do stuff
but I'm sure there are easier (maybe not as fast) ways.
At the moment, all solutions depend on converting the number to a string. While that works, you could do it completely numerically:
def check_digits(nbr, max=1):
nbr = abs(nbr) # only look at a positive numbers
while nbr != 0:
nbr, rem = divmod(nbr, 10)
if rem > max: return False
return True
The trick here is that each time the digit furthest to the right gets inspected. Once that's done, the number gets divided by 10, ignoring the remainder. So 1201 becomes 120, which becomes 12 and the cycle stops there, because divmod(12, 10) is (1, 2), and the remainder, 2, is bigger than your maximum digit.
Note that TigerhawkT3's answer and #John La Rooy's comment probably nailed it (upvoted) as it is by far the most Pythonic solution. All others, including mine, work for various programming languages, TigerhawkT3's solution uses Python exceptions to good effect.

SpecialStrings prob from topcoder srm 634 div 2

I python seems work with not big length of input string, but it failed with pretty long string. Here is the problem statement:
A string S is called special if it satisfies the following two properties:
Each character in S is either '0' or '1'.
Whenever S = UV where both U and V are nonempty strings, U is strictly smaller than V in lexicographic order.
For example, the string S = "00101" is special because we have "0" < "0101", "00" < "101", "001" < "01", and "0010" < "1".
You are given a string current that is guaranteed to be special. Let N be the length of current. Consider the lexicographically sorted list of all special strings of length N. Compute and return the string that comes immediately after current in this list. If current happens to be the last string in the list, return an empty string instead.
Here is my python code:
class SpecialStrings(object):
def findNext(self, current):
if current == '0':
return '1'
N = len(current)
iter_times = 2 ** N - int(current, 2) - 1
temp_current = current
for i in range(iter_times):
temp_s = self.get_next_string(temp_current)
if self.is_special(temp_s):
return temp_s
if temp_s[0] == '1':
return ''
temp_current = temp_s
return ''
def get_next_string(self, s):
next_string = bin(int(s, 2) + 1)
next_string = next_string[2:]
if len(next_string) < len(s):
temp_zero = '0' * (len(s) - len(next_string))
next_string = temp_zero + next_string
return next_string
def is_special(self, s):
for i in range(1, len(s)):
left = s[:i]
right = s[i:]
if left >= right:
return False
return True
I received abnormal termination with inputs "0111111111111111111111111111" and "001111111111111111111111111111111111111111". When I tried to test it locally with either of them, my computer memory was exhausted......
What is the problem in here? Is it because my algorithm is not efficient? How to solve it?
Thank you very much!!!!!
You're probably using Python2. In Py2, range() (your 8th line) returns a list. E.g. range(3) returns [0, 1, 2]. So range(iter_times) creates a large list that use up memory. The simple workaround: use xrange instead. Refer
But with xrange, you may soon get another error:
OverflowError: Python int too large to convert to C long
Because the argument passed to xrange should not exceed max signed long. E.g. in my machine xrange(0x7fffffff) is OK, while xrange(0x80000000) can trigger the error. Refer
Workaround: in your 8th line, use while True: instead. Because if the codes work as expected, the loop should anyway return result or empty string before hitting the end of for loop. So no need to use for loop.
The next trouble, when you use 'while True', input '0111111111111111111111111111' should work fine, but '001111111111111111111111111111111111111111' will keep calculating. That's the algorithm matter. You used brute force, long computation time is expected for large input. You may need to find pattern of special strings to improve the algorithm.

Categories

Resources