How can I make my function work for any number? - python-3.x

I am having some issues with some code I wrote for this problem:
“Write a function namedd calc that will evaluate a simple arithmetic expression. The input to your program will be a string of the form:
operand1 operator operand2
where operand1 and operand2 are non-negative integers and operator is a single-character operator, which is either +, -, or *. You may assume that there is a space between each operand and the operator. You may further assume that the input is a valid mathemat- ical expression, i.e. your program is not responsible for the case where the user enters gibberish.
Your function will return an integer, such that the returned value is equal to the value produced by applying the given operation to the given operands.
Sample execution:
calc("5 + 10") # 15
“You may not use the split or eval functions in your solution.
Hint: the hard part here is breaking the input string into its three component. You may use the find and rfind functions to find the position of the first and last space, and then use the slice operator (that is, s[startindex:endindex]) to extract the relevant range of characters. Be careful of off-by-one errors in using the slice operator.
Hint: it’s best to test your code as you work. The first step should be to break the input string into its three components. Write a program that does that, have it print out the operator and the two operands on separate lines, and test it until you are convinced that it works. Then, modifying it to perform the desired mathematical operation should be straightforward. Test your program with several different inputs to make sure it works as you expect.”
Here is my code:
def calc(exp):
operand1 = int(exp[:1])
operand2 = int(exp[4:6])
operator = exp[2:3]
if(operator == "+"):
addition = operand1+operand2
return addition
if(operator == "-"):
subtraction = operand1-operand2
return subtraction
if(operator == "*"):
multiplication = operand1*operand2
return multiplication
print(calc("5 + 10"))
print(calc("4 - 8"))
print(calc("4 * 3"))
My code does not fully meet the criteria of this question. It only works for single digit numbers. How can I make my code work for any number?
Like:
“504 + 507”
”5678 + 76890”
and so on?
Thank you. Any help is appreciated.

As the hint says, get the position of the first and last space of the expression, use it to extract the operand and the operators, and then evaluate accordingly.
def calc(exp):
#Get the position for first space with find
low_idx = exp.find(' ')
#Get the position for last space with rfind
high_idx = exp.rfind(' ')
#Extract operators and operand with slice, converting operands to int
operand1 = int(exp[0:low_idx])
operator = exp[low_idx+1:high_idx]
operand2 = int(exp[high_idx:])
result = 0
#Evaluate based on operator
if operator == '+':
result = operand1 + operand2
elif operator == '-':
result = operand1 - operand2
elif operator == '*':
result = operand1 * operand2
return result
print(calc("5 + 10"))
print(calc("4 - 8"))
print(calc("4 * 3"))
print(calc("504 + 507"))
print(calc("5678 + 76890"))
#15
#-4
#12
#1011
#82568

The answer is in the specification:
You may use the find and rfind functions to find the position of the first and last space, and then use the slice operator (that is, s[startindex:endindex]) to extract the relevant range of characters.
find and rfind are methods of string objects.

You could split it into three components using this code: (note: this doesn't use split or eval)
def splitExpression(e):
numbers = ["1","2","3","4","5","6","7","8","9","0"] # list of all numbers
operations = ["+","-","*","/"] # list of all operations
output = [] # output components
currentlyParsing = "number" # the component we're currently parsing
buildstring = "" # temporary variable
for c in e:
if c == " ":
continue # ignore whitespace
if currentlyParsing == "number": # we are currently parsing a number
if c in numbers:
buildstring += c # this is a number, continue
elif c in operations:
output.append(buildstring) # this component has reached it's end
buildstring = c
currentlyParsing = "operation" # we are expecting an operation now
else:
pass # unknown symbol!
elif currentlyParsing == "operation": # we are currently parsing an operation
if c in operations:
buildstring += c # this is an operation, continue
elif c in numbers:
output.append(buildstring) # this component has reached it's end
buildstring = c
currentlyParsing = "number" # we are expecting a number now
else:
pass # unknown symbol!
if buildstring: # anything left in the buffer?
output.append(buildstring)
buildstring = ""
return output
Usage: splitExpression("281*14") returns ["281","*","14"]
This function also accepts spaces between numbers and operations

You can simply take the string and use the split method for the string object, which will return a list of strings based on some separator.
For example:
stringList = "504 + 507".split(" ")
stringList will now be a list such as ["504", "+", "507"] due to the separator " " which is a whitespace. Then just use stringList[1] with your conditionals to solve the problem. Additionally, you can use int(stringList[0]) and int(stringList[2]) to convert the strings to int objects.
EDIT:
Now I realized that your problem said to use find() instead of split(). Simply use the logic above but instead find(" ") the first whitespace. You will then need to find the second whitespace by slicing past the first whitespace using the two additional arguments available for find().

You need to split the string out instead of hard coding the positions of the indexes.
When coding you want to try to make your code as dynamic as possible, that generally means not hard coding stuff that could be a variable or in this case could be grabbed from the spaces.
Also in the if statements I modified them to elif as it is all one contained statement and thus should be grouped.
def calc(exp):
vals = exp.split(' ')
operand1 = int(vals[0])
operand2 = int(vals[2])
operator = vals[1]
if operator == '+':
return operand1+operand2
elif operator == '-':
return operand1-operand2
else:
return operand1*operand2

Related

Replace string only if all characters match (Thai)

The problem is that มาก technically is in มาก็. Because มาก็ is มาก + ็.
So when I do
"แชมพูมาก็เยอะ".replace("มาก", " X ")
I end up with
แชมพู X ็เยอะ
And what I want
แชมพู X เยอะ
What I really want is to force the last character ก็ to count as a single character, so that มาก no longer matches มาก็.
While I haven't found a proper solution, I was able to find a solution. I split each string into separate (combined) characters via regex. Then I compare those lists to each other.
# Check is list is inside other list
def is_slice_in_list(s,l):
len_s = len(s) #so we don't recompute length of s on every iteration
return any(s == l[i:len_s+i] for i in range(len(l) - len_s+1))
def is_word_in_string(w, s):
a = regex.findall(u'\X', w)
b = regex.findall(u'\X', s)
return is_slice_in_list(a, b)
assert is_word_in_string("มาก็", "พูมาก็เยอะ") == True
assert is_word_in_string("มาก", "พูมาก็เยอะ") == False
The regex will split like this:
พู ม า ก็ เ ย อ ะ
ม า ก
And as it compares ก็ to ก the function figures the words are not the same.
I will mark as answered but if there is a nice or "proper" solution I will chose that one.

Recursive function how to manage output

I'm working on a project for creating some word list. I have a word and some rules, for example, this char % is for digit, while this one ^ for special character, for example January%%^ should create things like:
January00!
January01!
January02!
January03!
January04!
January05!
January06!
etc.
For now I'm trying to do it with only digit and create a recursive function, because people can add as many digits and special characters as they want
January^%%%^% (for example)
This is the first function I have created:
month = "January"
nbDigit = "%%%"
def addNumber(month : list, position: int):
for i in range(position, len(month)):
for j in range(0,10):
month[position] = j
if(position == len(month)-1):
print (''.join(str(v) for v in month))
if position < len(month):
if month[position+1] == "%":
addNumber(month, position+1)
The problem is for each % that I have there is another output (three %, three times as output January000-January999/January000-January999/January000-January999).
When I tried to add the new function special character it's even worse, because I can't manage the output since every word can't end with a special character or digit. (AddSpecialChar is also a recursive function).
I believe what you are looking for is the following:
month = 'January'
nbDigit = "%%"
def addNumbers(root: str, mask: str)-> list:
# create a list of words using root followed By digits
rslt = []
mxNmb = 0
for i in range(len(mask)):
mxNmb += 9 * 10**i
mxNmb += 1
for i in range(mxNmb):
word = f"{root}{((str(i).rjust(len(mask), '0')))}"
rslt.append(word)
return rslt
this will produce:
['January00',
'January01',
'January02',
'January03',
'January04',
'January05',
'January06',
'January07',
'January08',
'January09',
'January10',
'January11',
'January12',
'January13',
'January14',
'January15',
'January16',
'January17',
'January18',
'January19',
'January20',
'January21',
'January22',
'January23',
'January24',
'January25',
'January26',
'January27',
'January28',
'January29',
'January30',
'January31',
'January32',
'January33',
'January34',
'January35',
'January36',
'January37',
'January38',
'January39',
'January40',
'January41',
'January42',
'January43',
'January44',
'January45',
'January46',
'January47',
'January48',
'January49',
'January50',
'January51',
'January52',
'January53',
'January54',
'January55',
'January56',
'January57',
'January58',
'January59',
'January60',
'January61',
'January62',
'January63',
'January64',
'January65',
'January66',
'January67',
'January68',
'January69',
'January70',
'January71',
'January72',
'January73',
'January74',
'January75',
'January76',
'January77',
'January78',
'January79',
'January80',
'January81',
'January82',
'January83',
'January84',
'January85',
'January86',
'January87',
'January88',
'January89',
'January90',
'January91',
'January92',
'January93',
'January94',
'January95',
'January96',
'January97',
'January98',
'January99']
Adding another position to the nbDigit variable will produce the numeric sequence from 000 to 999

How can I change upper case to lower and vice versa?

I am playing around with a small script, just for fun. I'm trying to reverse the items in a string and witch the upper case to lower and lower to upper. The reverse part works, but the case part doesn't. What am I doing wrong here?
def reverse(s):
if len(s) == 0:
return s
else:
if s.lower():
s.upper()
else:
s.lower()
return reverse(s[1:]) + s[0]
mytxt = reverse("I wonder how this text looks like Backwards")
print(mytxt)
Here is my current output.
sdrawkcab ekil skool txet siht woh rednow I
str.lower does not return a boolean of whether it's lowercase or not. It returns a string in lowercase. It also doesn't change a string in place.
Since that is the case you need to check the character you are currently interested in.
In this case s[0]. Additionally, strings aren't mutable so you can't change them in place. You'll need a temp variable.
def reverse(s):
if len(s) == 0:
return s
else:
# The character of interest
char = s[0]
# If it's equal to the lowercase version of it
if char == char.lower():
# Change it to upper
char = char.upper()
else:
# and vice versa
char = char.lower()
return reverse(s[1:]) + char
mytxt = reverse("I wonder how this text looks like Backwards")
print(mytxt)
s.lower() and s.upper() do not modify s but instead return another string with all letters converted to lowercase or uppercase, respectively. They don't return booleans, either (which is done by s.islower() and s.isupper()).
If you want to rewrite s, you must construct a new string from the return values.
def reverse(s):
if len(s) == 0:
return s
else:
s0 = s[0]
if s0.islower():
s0 = s0.upper()
elif s0.isupper():
s0 = s0.lower()
return reverse(s[1:]) + s0
mytxt = reverse("I wonder how this text looks like Backwards")
print(mytxt)
Here I checked for both islower and isupper, because both return False in the absence of cased characters (e.g. "0".islower() and "0".isupper() are both false).
str.lower() and str.upper() return a copy of the string converted to lower and upper case. To check whether a string is lower or uppercase, use str.islower() and str.isupper()
Python also has str.swapcase() to do exactly what you want.
Reversing a string can be done simply by using the slice notation, no need for recursion or loops. Your code could be simplified to something like:
def swapcasereverse(s):
return s[::-1].swapcase()
If you want to write your own code for swapcase as an exercise, here's a pythonic way:
def swapcasereverse(s):
newlist = [c.upper() if c.islower() else c.lower() for c in reversed(s)]
return "".join(newlist)
This function uses a list comprehension to
iterate over s in reverse order, with each character going in c
if c is lowercase, adds c.upper() to the list
otherwise, adds c.lower() to the list
Joins the list with "" to make a string, returns the joined string
def reverse(s):
if len(s) == 0:
return s
else:
if s.lower():
s.upper()
else:
s.lower()
return reverse(s[1:]) + s[0]
mytext = reverse("I wonder how this text looks like Backwards").swapcase()
print(mytext)

Evaluating a mathematical expression without eval() on Python3 [duplicate]

This question already has answers here:
Evaluating a mathematical expression in a string
(14 answers)
Closed 10 months ago.
I'm working on a "copy-paste calculator" that detects any mathematical expressions copied to the system clipboard, evaluates them and copies the answer to the clipboard ready to be pasted. However, while the code uses the eval()-function, I'm not terribly concerned considering the user normally knows what they are copying. That being said, I want to find a better way without giving the calculations a handicap (= eg. removing the ability to calculate multiplications or exponents).
Here's the important parts of my code:
#! python3
import pyperclip, time
parsedict = {"×": "*",
"÷": "/",
"^": "**"} # Get rid of anything that cannot be evaluated
def stringparse(string): # Remove whitespace and replace unevaluateable objects
a = string
a = a.replace(" ", "")
for i in a:
if i in parsedict.keys():
a = a.replace(i, parsedict[i])
print(a)
return a
def calculate(string):
parsed = stringparse(string)
ans = eval(parsed) # EVIL!!!
print(ans)
pyperclip.copy(str(ans))
def validcheck(string): # Check if the copied item is a math expression
proof = 0
for i in mathproof:
if i in string:
proof += 1
elif "http" in string: #TODO: Create a better way of passing non-math copies
proof = 0
break
if proof != 0:
calculate(string)
def init(): # Ensure previous copies have no effect
current = pyperclip.paste()
new = current
main(current, new)
def main(current, new):
while True:
new = pyperclip.paste()
if new != current:
validcheck(new)
current = new
pass
else:
time.sleep(1.0)
pass
if __name__ == "__main__":
init()
Q: What should I use instead of eval() to calculate the answer?
You should use ast.parse:
import ast
try:
tree = ast.parse(expression, mode='eval')
except SyntaxError:
return # not a Python expression
if not all(isinstance(node, (ast.Expression,
ast.UnaryOp, ast.unaryop,
ast.BinOp, ast.operator,
ast.Num)) for node in ast.walk(tree)):
return # not a mathematical expression (numbers and operators)
result = eval(compile(tree, filename='', mode='eval'))
Note that for simplicity this allows all the unary operators (+, -, ~, not) as well as the arithmetic and bitwise binary operators (+, -, *, /, %, // **, <<, >>, &, |, ^) but not the logical or comparison operators. If should be straightforward to refine or expand the allowed operators.
without using eval, you'd have to implement a parser, or use existing packages like simpleeval (I'm not the author, and there are others, but I have tested that one successfully)
In one line, plus import:
>>> from simpleeval import simpleeval
>>> simpleeval.simple_eval("(45 + -45) + 34")
34
>>> simpleeval.simple_eval("(45 - 22*2) + 34**2")
1157
now if I try to hack the calculator by trying to import a module:
>>> simpleeval.simple_eval("import os")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "K:\CODE\COTS\python\simpleeval\simpleeval.py", line 466, in simple_eval
return s.eval(expr)
File "K:\CODE\COTS\python\simpleeval\simpleeval.py", line 274, in eval
return self._eval(ast.parse(expr.strip()).body[0].value)
AttributeError: 'Import' object has no attribute 'value'
Caught! the cryptic error message comes from the fact that simpleeval can evaluate variables that you can optionally pass through a dictionary. Catch AttributeError exception to intercept wrongly formed expressions. No need for eval for that.
By native Python3: without using inbuilt function
input_string = '1+1-1*4+1'
result = 0
counter = -1
for ch in range(len(input_string)):
if counter == ch:
continue
if input_string[ch] in ['-', '+', '/', '*', '**']:
next_value = int(input_string[ch+1])
if input_string[ch] == '-':
result -= next_value
counter = ch+1
elif input_string[ch] == '+':
result += next_value
counter = ch+1
elif input_string[ch] == '*':
result *= next_value
counter = ch+1
elif input_string[ch] == '/':
result /= next_value
counter = ch+1
elif input_string[ch] == '**':
result **= next_value
counter = ch+1
else:
result = int(input_string[ch])
print(result)
Output : 
The original string is : '1+1-1*4+1'
The evaluated result is : 5

how to convert decimal to binary by using repeated division in python

how to convert decimal to binary by using repeated division in python?
i know i have to use a while loop, and use modulus sign and others {%} and {//} to do this...but i need some kind of example for me to understand how its done so i can understand completely.
CORRECT ME, if I'm wrong:
number = int(input("Enter a numberto convert into binary: "))
result = ""
while number != 0:
remainder = number % 2 # gives the exact remainder
times = number // 2
result = str(remainder) + result
print("The binary representation is", result)
break
Thank You
Making a "break" without any condition, makes the loop useless, so the code only executes once no matter what.
-
If you don't need to keep the original number, you can change "number" as you go.
If you do need to keep the original number, you can make a different variable like "times".
You seem to have mixed these two scenarios together.
-
If you want to print all the steps, the print will be inside the loop so it prints multiple times.
If you only want to print the final result, then the print goes outside the loop.
while number != 0:
remainder = number % 2 # gives the exact remainder
number = number // 2
result = str(remainder) + result
print("The binary representation is", result)
-
The concatenation line:
Putting the print inside the loop might help you see how it works.
we can make an example:
the value in result might be "11010" (a string, with quotes)
the value in remainder might be 0 (an integer, no quotes)
str(remainder) turns the remainder into a string = "0" instead of 0
So when we look at the assignment statement:
result = str(remainder) + result
The right side of the assignment operator = is evaulated first.
The right side of the = is
str(remainder) + result
which, as we went over above has the values:
"0" + "11010"
This is string concatenation. It just puts one string on the end of the other one. The result is:
"0 11010"
"011010"
That is the value evaluated on the right side of the assignment statement.
result = "011010"
Now that is the value of result.
B_Number = 0
cnt = 0
while (N != 0):
rem = N % 2
c = pow(10, cnt)
B_Number += rem * c
N //= 2
# Count used to store exponent value
cnt += 1
return B_Number

Resources