# A simple pocket calculator

Posted on

Problem

I was searching the internet for some Python exercises, and I’ve found a lab assignment from University of Toronto. Here’s the major of it:

Question 1.
Welcome Message In the if `__name__ == "__main__"` block, write code that displays the following: Welcome to the calculator program. Current value: 0

Question 2.
Displaying the Current Value Write a function whose signature is `display_current_value()`, and which displays the current value in the calculator. In the if `__name__ == "__main__"` block, test this function by calling it and observing the output.

Question 3.
Addition Write a function whose signature is `add(to_add)`, and which adds `to_add` to the current value in the calculator, and modifies the current value accordingly. In the if `__name__ == "__main__"` block, test the function add by calling it, as well as by calling `display_current_value()`.
Hint: when modifying global variables from within functions, remember to declare them as global.

Question 4.
Multiplication Write a function whose signature is `mult(to_mult)`, and which multiplies the current value in the calculator by `to_mult`, and modifies the current value accordingly. In the if `__name__ == "__main__"` block, test the function.

Question 5.
Division Write a function whose signature is `div(to_div)`, and which divides the current value in the calculator by `to_div`, and modifies the current value accordingly. In the if `__name__ == "__main__"` block, test the function. What values of `to_div` might cause problems? Try them to see what happens.

Question 6.
Memory and Recall Pocket calculators usually have a memory and a recall button. The memory button saves the current value and the recall button restores the saved value. Implement this functionality.

Question 7.
Undo Implement a function that simulates the Undo button: the function restores the previous value that appeared on the screen before the current one.

Here’s my solution:

``````current_value = 0
memory = current_value
memo = [current_value]

def is_divisible(x, y):
"""Return whether x is divisible by y. """
return x % y == 0

def update_list(lst, value):
"""Updates a list with an item. """
lst.append(value)

def display_current_value():
"""Prints the current value of the calculator. """
print('Current value:', current_value)

"""Adds a number to the current value of the calcalutor. """
global current_value

update_list(memo, current_value)

def mult(to_mult):
"""Multiplies the current value of the calcalutor by a number. """
global current_value

current_value *= to_mult

update_list(memo, current_value)

def div(to_div):
"""Divides the current value of the calcalutor by a number. """
global current_value

if is_divisible(current_value, to_div):
current_value //= to_div
else:
current_value /= to_div

update_list(memo, current_value)

def store():
"""Stores the current value. """
global memory

memory = current_value

def recall():
"""Recalls the saved value. """
return memory

def undo():
"""Restores the previous value. """
global memo

if len(memo) >= 2:
global current_value

current_value = memo[-2]

memo.pop(-2)

def main():
print('Welcome to the calculator program.' )
display_current_value()

display_current_value()

mult(2)
display_current_value()

div(3)
display_current_value()

for iteration in range(3):
undo()
display_current_value()

if __name__ == '__main__':
main()
``````

Is it possible to implement `undo` without dealing with other functions such as `add`? Is the code well-documented?

Please point out any bad practice or any bug I’ve made. Please point out how somethings can be done better. And please note that I’m a beginner and a hobbyist.

Solution

• You want to check that your divisor != 0, or catch ZeroDivisionError exception.
• You may want to rethink your liberal use of globals. You can have the same behaviour, and a more robust code, if you send the value in the global to the function, and assign the result of the function.

Here’s the code with the above implemented:

``````\$ cat calc.py
def is_divisible(x, y):
"""Return whether x is divisible by y. """
return x % y == 0

def update_list(lst, value):
"""Updates a list with an item. """
lst.append(value)
return lst

def display_current_value(current_value):
"""Prints the current value of the calculator. """
print('Current value:', current_value)

"""Adds a number to the current value of the calcalutor. """

memo = update_list(memo, current_value)
return current_value

def mult(to_mult, current_value, memo):
"""Multiplies the current value of the calcalutor by a number. """

print (current_value,"*",to_mult)
current_value *= to_mult

memo = update_list(memo, current_value)
return current_value

def div(to_div, current_value, memo):
"""Divides the current value of the calcalutor by a number. """

print (current_value,"/",to_div)
if to_div != 0:
if is_divisible(current_value, to_div):
current_value //= to_div
else:
current_value /= to_div
else:
print ("you tried division by 0")

memo = update_list(memo, current_value)
return current_value

def undo(current_value, memo):
"""Restores the previous value. """

print("undo")
if len(memo) >= 2:

memo.pop()
current_value = memo[-1]

return current_value, memo

def main(current_value, memory, memo):
print('Welcome to the calculator program.' )
display_current_value(current_value)

display_current_value(current_value)

current_value = mult(2, current_value, memo)
display_current_value(current_value)

current_value = div(3, current_value, memo)
display_current_value(current_value)

for iteration in range(3):
# print("memo",memo)
current_value, memo = undo(current_value, memo)
display_current_value(current_value)

if __name__ == '__main__':
current_value = 0
memory = current_value
memo = [current_value]
main(current_value, memory, memo)

\$ python3 calc.py
Welcome to the calculator program.
Current value: 0
0 + 6
Current value: 6
6 * 2
Current value: 12
12 / 3
Current value: 4
undo
Current value: 12
undo
Current value: 6
undo
Current value: 0
``````

Note: for completion sake, you may want to add a function for substruction.

With your three globals, it’s not clear what `memo` is from looking at it. Try `previous_values` instead. Additionally, populating the list with `memory` at the beginning makes me think they’re related, which I’m not sure is actually the case.

boardrider suggests passing your globals as arguments to functions. I agree that this would be a good idea. Another approach would to just wrap it all in a `Calculator` class. Classes are good for managing state.

Your undo function is a bit weird:

``````def undo():
"""Restores the previous value. """
global memo

if len(memo) >= 2:
global current_value

current_value = memo[-2]

memo.pop(-2)
``````

The previous current_value remains in memory, which makes me not trust it. What happens if you repeatedly undo, then operate? I haven’t stepped through to verify, but I suspect a bug.

``````def undo():
if len(memo) >= 2:
global current_value
memo.pop()
current_value = memo[-1]
``````

This would maintain the invariant that the current value is always the last item in the list.

My final comment I am much less unsure of, and is probably down to a matter of personal taste:

``````if is_divisible(current_value, to_div):
As another idea, you could do something like `if current_value == int(current_value): current_value = int(current_value)` after each op.