-1

How do i create a function that takes a list with alternating math symbols (plus, minus) and integers then return the result. For example, 4, '+', 5, '-', 1 should return 8.

  • 1
    So should `[1, '+', 2, '*', 3]` return 7 (`1+2*3`), or 9 (`(1+2)*3`)? – abarnert Dec 06 '14 at 01:59
  • The answer to this question should answer your question: http://stackoverflow.com/questions/1740726/python-turn-string-into-operator Basically associate the string "+" with the operator using a dictionary with "+" as the key and `operator.add` as the value. – Vorticity Dec 06 '14 at 01:56
  • `eval(''.join(str(t) for t in seq))` P.S. this seems like a bad idea – Ryan Haining Dec 06 '14 at 02:01
  • @RyanHaining: That's the simplest (but obviously not best) way to get `9`, but it's going to give the wrong answer if he wants 7 (which is pretty simple to do manually). – abarnert Dec 06 '14 at 02:02
  • 2
    @abarnert I think OP means that it *only* contains plus and minus when saying "alternating" symbols – Ryan Haining Dec 06 '14 at 02:03
  • 1
    @abarnert If the OP has some obscure requirement to break the conventional order of operations, they should say so. Otherwise, we should assume normal mathematical conventions. If the function accepts a full expression as a list, most developers would probably expect it to be processed as a whole, rather than "in order". – jpmc26 Dec 06 '14 at 02:44
  • Why not just answer the guy's question as asked? – Travis Griggs Dec 08 '14 at 15:10
  • @jpmc26: The "alternating math symbols and integers" implies that he hasn't really thought about the conventional precedence, and may not even understand the issue. After all, using conventional precedence without allowing parentheses makes a calculator almost useless. That's why I think it's important to ask him. – abarnert Dec 08 '14 at 21:50

3 Answers3

1

I'll give you a one liner. If this is homework, your teacher's going to raise his eyebrow's at this. Given

words = [4, '+', 5, '-', 1]

then

result = sum(digit * (-1 if sign == '-' else 1) for digit, sign in zip(words[0::2], ['+'] + words[1::2]))

What we're leveraging here is that you said just '+' and '-' operands. This type of number sentence can essentially be rewritten as (+4) + (+5) + (-1). In other words, we just view the +/- as a sign indicator, and then sum them all.

So decomposing the above...

# Extract a slice for the digits
digits = words[0::2] # --> [4, 5, 1]
# Extract a slice for the signs
signs = words[1::2]  # --> ['+', '-']
# Our signs needs to have the same size as digits, there's an implicit '+' preceding the first digit
signs = ['+'] + signs # --> ['+', '+', '-']
# use the if else and a list comprehension to build a derived list of numerical signs
numericalSigns = [-1 if each == '-' else 1 for each in signs] # --> [1, 1, -1]
# Use zip to combine the two separate lists into a single list of pairs (tuples)
pairs = list(zip(digits, numericalSigns)) # --> [(4, 1), (5, 1), (1, -1)]
# Use a list comprehension to multiple each tuple
signedDigits = [digit * sign for digit, sign in pairs] # --> [4, 5, -1]
# Use the sum builtin to add them up
sum(signedDigits) # --> 8
Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
0

If you have anything beyond + and -, what you're asking for is actually ambiguous. For example, consider this list:

[1, '+', 2, '*', 3]

That could evaluate to 9, because 1+2 is 3, then 3*3 is 9.

Or it could evaluate to 7, because 1+2*3 is 7 (because * binds tighter than + in Python—and in almost all algebraic notation systems).


The 9 is pretty easy to get.

First you need to know how to convert each operator to a function. As Vorticity points out, this answer covers that nicely, so let's borrow the ops dictionary from that answer.

Then you just need to walk the list and do something which each value. You can either use the rule "strings are operators, ints are numbers", or you can use the rule "odd positions are operators, even positions are numbers". Let's go with the first one.

result, op = None, None
for element in func:
    if isinstance(element, int):
        if op:
            result = op(result, element)
        else:
            result = element
    elif isinstance(element, str):
        op = ops[element]
    else:
        raise ValueError('{} is neither an operator nor a number!'.format(element))

To get the 7, you're going to have to implement operator precedence in some way. That means you can't just evaluate every operator as soon as you see it; if you see [2, '+', 3, …] you don't actually know that you need to add 2 and 3 until you whether the next operator is a + or - (in which case you should) or a * or / (in which case you can't).

One way to solve this is to keep a stack of operator/operand pairs instead of just a single operator. Push each pair onto the stack, then, when you see a new operator (or the end of the list), pop off and process all operations with equal or higher precedence.

But the usual way to handle this is to not do it linearly, and instead write a simple operator-precedence parser that builds a tree out of the linear string, and then evaluate that tree from bottom up. You can prove that (under certain conditions) the two are equivalent, but the tree is just a much easier way of thinking about things.

Community
  • 1
  • 1
abarnert
  • 354,177
  • 51
  • 601
  • 671
-1

That depends on the order of operation you are looking for.

1+2*3 could be either 9, 1+2 = 3, 3*3 = 9(straight order of operation)

Or could be 7, 2*3 = 6 + 1 = 7(math order of operation)

to get the 9:

items = [1, '+', 2, '*', 3]
last = None
operator = None
for it in items:
    if last == None:
        last = it
    else:
        if it in ["+","*"]: # add others symbol at will, be sure to convert to python symbol
            operator = it
        else:
            last = eval(str(last)+operator+str(it))
print last

output:

9


To get the 7:

This one is simplier, just use eval:

items = [1, '+', 2, '*', 3]
print eval("".join(str(i) for i in items))

output:

7


By the way, eval is not very secure, a malicious user can insert nasty codes to your system.

f.rodrigues
  • 3,499
  • 6
  • 26
  • 62