Hacker News new | past | comments | ask | show | jobs | submit login
How To Write A Calculator in 70 Python Lines (erezsh.com)
74 points by gklein on Feb 23, 2013 | hide | past | favorite | 31 comments



How to Write a Calculator in 1 Python Line

    print input()

    > 25*4-50
    > 50


Q. How to write code?

A. I don't need to; other people will write it for me.

That's fine, I guess. I prefer to understand.


How to get things done? Use existing tools unless they are not suitable.


...100? Your calculator seems broken :)


haha thanks, fixed


It accepts more than just numbers though:

  >>> print input()
  "a"+"b"
  ab


  try:print(lambda x:eval(x)if all(i in'0123456789.*/+-'for i in x)else'syntax error')(raw_input())
  except:print'error'


Mathematically, this is wrong.. a + b is not ab.


It's not symbolic...


Of course. I understand that "a" + "b" when taken to mean "concatenate the strings" is "ab" but OP's post was a calculator. It would not have yielded this result.


it's a feature


Or, from a bash command line,

$ python -c "print input('> ')" > 2+3*5 17


or just 'python'


The. Best. Comment. Ever. :-)


Very cool.

Thanks for the cool post. I am not sure I got the difference between the LL and LR parser. Is what you have above an LR parser?

Also, why did you choose to represent both + and - as "ADD" tokens (and * and / as "MUL") is this to enforce evaluation priority? It would be interesting to see if you can add or ^ as an exponent for this calculator.

Maybe you intended this post strictly as as an educational post, but I think doing parsing right (and from first principles) is a really cool thing to have. Check out how khan-exercises framework uses to parse math expressions into an AST: https://github.com/Khan/khan-exercises/blob/master/utils/mat...


An LR-parser tries to reduce the input over and over again into rules, eventually ending with the 'start' rule. So a+b+c+d becomes [add]+c+d -> [add]+d -> [add] -> [start]

An LL-parser tries to expand the initial rule into a more complex rule structure, until it matches the input. So to match a+b+c+d it will do [start] -> [add] -> [add] + [num] -> [add] + [num] + [num] -> etc.

What I wrote is an LL-parser, simply because it's much much simpler to write and to understand.

Yes, both ADD and MUL are used for precedence. Since any list of +- or of */ will evaluate correctly if reduced from left to right, I didn't mind grouping them together and making my life easier (and shorter).

It was strictly educational, and also a shtick; a short code hack. If I was to write an actual parser (and I don't think I would ever try to), it would look very different!


Very nicely done!

I think a precedence parser could be even shorter, though I don't seem to have one handy in Python. Pratt parsing is similar, though: https://github.com/darius/sketchbook/blob/master/parsing/pra... And here's precedence parsing in C: http://wry.me/~darius/hacks/dcalc.c (I learned this method from Dave Gillespie, author of Gnu Calc.)

Finally, using a small parsing library very similar to the scheme in this post: https://github.com/darius/peglet/blob/master/examples/infix....


The calculator seems to miss the point of recursive descent parsing. The parser should follow naturally from the grammar. As an example, here is a 52 line python calculator:

https://gist.github.com/ascv/5022712

The idea isn't to 1-up the OP but rather to showcase the technique of recursive descent parsing. The wikipedia entry for recursive descent parsing also has a very nice example.


Well, to be fair, your calculator seems to miss the point of being a calculator...

    > 10/2*2
    2.5


Clearly a bug. Thanks when I have more time I will fix it. I didn't give much thought to operator precedence, I assumed the user would specify it using parentheses.

The idea is what is important though. Recursive descent parsing uses the functions that correspond to types in the grammar to mutually call each other thus parsing the expression recursively in a descending fashion.


I decided to simply add to the description, "use parentheses to specify operator precedence."


this reminded me of something similar i did (i'm afraid my recursive decent parser is probably more opaque) - differentiating numerical expressions in python. http://www.acooke.org/cute/Differenti0.html


Here is a similar calculator implemented in 58 languages including in 16 lines of Python code :)

http://www.math.bas.bg/bantchev/place/rpn/rpn.impl.html


This is an RPN calculator, which means you can basically execute the input using the token stream directly. TFA shows executing a context free language, which requires parsing, explaining the program being an order of magnitude larger.


FYI this http://www.imgur.com/5Ge93Xc.png is what it looks like on my Nexus 4, perhaps it's worth considering a more mobile-friendly layout ;)


I wonder if there's a way to add algebraic equation solving to this, or something like it, easily.


Not easily, and not in 70 lines. But there's the Python library "sympy" which can do symbolic algebraic equation solving (and many other things):

    from sympy import *
    var('x,b,c')
    print solve(a*x**2+b*x+c,x)
Result:

    [(-b + (-4*a*c + b**2)**(1/2))/(2*a), -(b + (-4*a*c + b**2)**(1/2))/(2*a)]


This article seems rather un-Zen

>>> import this

... though rules are made to be broken


Why is it un-zen?


I think it is not too unzen in its aim and method but if you ask there many little tricks that I'd say are unzen. First, give all the code a pass of autopep8 comb. Second, avoid things like "lambda (op,num): (num, -num)[...], just use "x if y else z"

Then you wouldn't be too far from Norvig's little educational gems.


I suppose I got carried away with the lambdas :)

Thank you, you gave me a great compliment. Norvig's short spellchecker was very inspirational for me.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: