Currently taking the freecodecamp python and need help refactoring my solution - python

I tried my hand at the freecodecamp python algorithm and I came up with a solution for the first question but need help refactoring it. Here is the arithmetic arranger question click here to access the question
The solution can be found below:
import operator
ops = {"+": operator.add, "-": operator.sub, "*": operator.mul}
def arithmetic_arranger(problems, solver=False):
# Check problems does not exceed the given max(5)
if len(problems) > 5:
return "Error: Too many problems."
toptier = ""
bottomtier = ""
lines = ""
totals = ""
for n in problems:
fnumber = n.split()[0]
operator = n.split()[1]
snumber = n.split()[2]
# Handle errors for input:
if operator != "+" and operator != "-":
return "Error: Operator must be '+' or '-'."
if not fnumber.isdigit() or not snumber.isdigit():
return "Error: Numbers must only contain digits."
if len(fnumber) > 4 or len(snumber) > 4:
return "Error: Numbers cannot be more than four digits"
# Get total of correct function
total = ops[operator](int(fnumber), int(snumber))
# Get distance for longest operator
operatorDistance = max(len(fnumber), len(snumber)) + 2
snumber = operator + snumber.rjust(operatorDistance - 1)
toptier = toptier + fnumber.rjust(operatorDistance) + (4 * " ")
bottomtier = bottomtier + snumber + (4 * " ")
lines = lines + len(snumber) * "_" + (4 * " ")
totals = totals + str(total).rjust(operatorDistance) + (4 * " ")
if solver:
print(toptier)
print(bottomtier)
print(lines)
print(totals)
if __name__ == "__main__":
arithmetic_arranger(["32 + 698", "3801 - 2", "45 + 43", "123 + 49"])

Moved the post to the CodeReview community as it already works and I just need help refactoring it. If interested click here to follow and help there instead :)

Related

Why is this code showing number 2 multiple times?

So I'm trying to make a calculator but when i do plus (also with other things but for example) it does work but after the outcome comes it asks for number 2 again, I just want the code to start again.
this is the plus piece of the code:
q = input(str("Wil je de bewerkingsteken legende zien? (j/n): "))
if q == "J" or q == "j" :
print ("\nplus = + ")
print ("min = -")
print ("maal = X")
print ("delen door = :")
print ("quadrateren = Q")
print ("tot de kracht van = P")
print ("Worteltrekken = W")
print ("Procent = %")
num1 = float(input("\n Nummer 1: "))
bew = input("\n Bewerkingsteken: ")
num1_word = (str(num1))
if bew == "+" :
plus_num2 = input(float("\nNummer 2: "))
plus_num2_con = (str(plus_num2))
plus_out = (num1 + plus_num2)
plus_out1 = (str(plus_out))
print ("\n" + num1_con +" + " + num2_con + " = " + plus_out1)
First, you write the input wrong for plus_num2. Try this;
plus_num2 = float(input("\nNummer 2: "))
Second, you define the number's name different from last print function. Try This;
print ("\n" + num1_word +" + " + plus_num2_con + " = " + plus_out1)
Third, if you want to start the code again you can add while True on first line.

How would I make it to where if I get the right answer it will print something

import random
from browser import timer
operators = ['*', '/', '+', '-']
number = input('How many problems would you like?')
number = int(number)
counter = 1
while counter <= number:
first = random.randint(0,10)
second = random.randint(0,10)
randoperator = random.choice(operators)
problem = '{} {} {} {}'.format(first, randoperator, second, "= ")
answer = input(problem)
correct = problem
counter+=1
I have tried putting this in but it doesn't run anything
if problem == answer:
print("Correct!")
You need to actually do the calculation to find out what the answer is. Here's a quick and dirty way to do that:
import random
operators = ['*', '/', '+', '-']
number = input('How many problems would you like?')
number = int(number)
counter = 1
while counter <= number:
first = random.randint(0,10)
second = random.randint(0,10)
randoperator = random.choice(operators)
problem = '{} {} {}'.format(first, randoperator, second)
answer = input(problem + ' = ')
if eval(problem) == float(answer):
print("Correct!")
counter+=1
Using eval is not a great idea for reasons outlined in the answers to this question. In your case, you already know the two integers and the operator, so finding the expected answer without eval is pretty easy. Say you define a function that can do this for you:
def arithmetic(op, a, b):
if op == "+":
return a + b
elif op == "-":
return a - b
elif op == "*":
return a * b
elif op == "/":
return a / b
Then call this function to get the expected answer and compare that with the answer that the user gave,
while counter <= number:
first = random.randint(0,10)
second = random.randint(0,10)
randoperator = random.choice(operators)
problem = '{} {} {}'.format(first, randoperator, second)
answer = input(problem + ' = ')
if arithmetic(randoperator, first, second) == float(answer):
print("Correct!")
counter+=1

How to correct addition of parentheses to not use parentheses unnecessarily python3

I am making a program which converting Prefix to Infix and I have a problem with putting brackets to expression.
For converting, I use classic stack algorithm. Here is my function for brackits.
Input:
//+86 16 67/*/31 53 85 15
My output:
((86+16)/67)/(((31/53)*85)/15)
Expecting output :
(86+16)/67/(31/53*85/15)
def breckets(operand1, operand2, operace):
res = ""
if (operand1.isdigit()) and (operand2.isdigit()):
res = operand1 + operace + operand2
elif (operand1.isdigit()) and (not operand2.isdigit()):
if operace == "+":
res = operand1 + operace + operand2
else:
res = operand1 + operace + "(" + operand2 + ")"
elif (not operand1.isdigit()) and (operand2.isdigit()):
if prior(operace) != 0:
res = "(" + operand1 + ")" + operace + operand2
else:
res = operand1 + operace + operand2
else:
res = "(" + operand1 + ")" + operace + "(" + operand2 + ")"
return res
def prior(a):
prior = None
if a in "+-":
prior = 0
elif a in "*/":
prior = 1
elif a in "^":
prior = 2
else:
print("Something went wrong")
exit()
return prior
But I must not use parentheses unnecessarily, can anybody advice me something please?
The code below can get what you expect. but I am not sure if the algorithm is right as I am not familiar with the data structure.
I use a basic function to sort the list several times, instead of sorting the list strictly from right to left, you can change some lines to break the while loop to make it so if that is what you want.
Anyway hope this can help you somehow.
operators = "+-*/^"
# presume the input is a finely spaced str that can be splitted into a proper list
# ipt = "/ * + 86 16 67 / * / + 31 53 85 15 / / 33 45 74" # for testing
ipt = "/ / + 86 16 67 / * / 31 53 85 15"
ip = ipt.split()
def sorting(lst):
res = []
i = len(lst)-1 # check list items from right to left
while i >= 0:
if i >= 3:
if lst[i-2] in operators:
# check if the operator is followed by two numbers
if lst[i-1] not in operators and lst[i] not in operators:
# check if the operator is + | -, the result should be in parentheses
if lst[i-2] in "+-":
res.append("(" + lst[i-1] + lst[i-2] + lst[i] + ")")
i -=3
continue
# if the operator is following another operator, the result shouldn't be in parentheses
if lst[i-3] in operators:
res.append(lst[i-1] + lst[i-2] + lst[i])
i -=3
continue
# if the operator is following a number, the result shouldn be in parentheses
else:
res.append("(" + lst[i-1] + lst[i-2] + lst[i] + ")")
i -= 3
continue
# this is to check the first item of the list is an operator and followed by two numbers
elif i == 2:
if lst[i-2] in operators:
if lst[i-1] not in operators and lst[i] not in operators:
res.append(lst[i-1] + lst[i-2] + lst[i])
i -=3
continue
res.append(lst[i])
i -= 1
# as all items are appending to the new list, so the positions are totally reversed
return list(reversed(res))
def no_more_operators(lst):
# to check if the lst is sortable
# one scenario is there are exccesive numbers, but no operators
# the current function can only check if any operators is in the list
for op in operators:
if op in lst:
return False
return True
def no_more_numbers(lst):
# to check if the lst is sortable
# one scenario is there are exccesive operators, but no numbers
# the current function can only check if any number is in the list
for i in lst:
if i.isnumeric():
return False
return True
# keep sorting the list until there is no more numbers or n0 more operators
while not no_more_numbers(ip) or not no_more_operators(ip):
ip = sorting(ip)
print(ip)
output:
['/', '/', '(86+16)', '67', '/', '*', '31/53', '85', '15']
['/', '(86+16)/67', '/', '31/53*85', '15']
['/', '(86+16)/67', '(31/53*85/15)']
['(86+16)/67/(31/53*85/15)']

converting infix to prefix in python

I am trying to write an Infix to Prefix Converter where e.g. I would like to convert this:
1 + ((C + A ) * (B - F))
to something like:
add(1, multiply(add(C, A), subtract(B, F)))
but I get this instead :
multiply(add(1, add(C, A), subtract(B, F)))
This is the code I have so far
postfix = []
temp = []
newTemp = []
def textOperator(s):
if s is '+':
return 'add('
elif s is '-':
return 'subtract('
elif s is '*':
return 'multiply('
else:
return ""
def typeof(s):
if s is '(':
return leftparentheses
elif s is ')':
return rightparentheses
elif s is '+' or s is '-' or s is '*' or s is '%' or s is '/':
return operator
elif s is ' ':
return empty
else :
return operand
infix = "1 + ((C + A ) * (B - F))"
for i in infix :
type = typeof(i)
if type is operand:
newTemp.append(i)
elif type is operator:
postfix.append(textOperator(i))
postfix.append(newTemp.pop())
postfix.append(', ')
elif type is leftparentheses :
newTemp.append(i)
elif type is rightparentheses :
next = newTemp.pop()
while next is not '(':
postfix.append(next)
next = newTemp.pop()
postfix.append(')')
newTemp.append(''.join(postfix))
while len(postfix) > 0 :
postfix.pop()
elif type is empty:
continue
print("newTemp = ", newTemp)
print("postfix = ", postfix)
while len(newTemp) > 0 :
postfix.append(newTemp.pop())
postfix.append(')')
print(''.join(postfix))
Can someone please help me figure out how I would fix this.
What I see, with the parenthetical clauses, is a recursive problem crying out for a recursive solution. The following is a rethink of your program that might give you some ideas of how to restructure it, even if you don't buy into my recursion argument:
import sys
from enum import Enum
class Type(Enum): # This could also be done with individual classes
leftparentheses = 0
rightparentheses = 1
operator = 2
empty = 3
operand = 4
OPERATORS = { # get your data out of your code...
"+": "add",
"-": "subtract",
"*": "multiply",
"%": "modulus",
"/": "divide",
}
def textOperator(string):
if string not in OPERATORS:
sys.exit("Unknown operator: " + string)
return OPERATORS[string]
def typeof(string):
if string == '(':
return Type.leftparentheses
elif string == ')':
return Type.rightparentheses
elif string in OPERATORS:
return Type.operator
elif string == ' ':
return Type.empty
else:
return Type.operand
def process(tokens):
stack = []
while tokens:
token = tokens.pop()
category = typeof(token)
print("token = ", token, " (" + str(category) + ")")
if category == Type.operand:
stack.append(token)
elif category == Type.operator:
stack.append((textOperator(token), stack.pop(), process(tokens)))
elif category == Type.leftparentheses:
stack.append(process(tokens))
elif category == Type.rightparentheses:
return stack.pop()
elif category == Type.empty:
continue
print("stack = ", stack)
return stack.pop()
INFIX = "1 + ((C + A ) * (B - F))"
# pop/append work from right, so reverse, and require a real list
postfix = process(list(INFIX[::-1]))
print(postfix)
The result of this program is a structure like:
('add', '1', ('multiply', ('add', 'C', 'A'), ('subtract', 'B', 'F')))
Which you should be able to post process into the string form you desire (again, recursively...)
PS: type and next are Python built-ins and/or reserved words, don't use them for variable names.
PPS: replace INFIX[::-1] with sys.argv[1][::-1] and you can pass test cases into the program to see what it does with them.
PPPS: like your original, this only handles single digit numbers (or single letter variables), you'll need to provide a better tokenizer than list() to get that working right.

Python Can't find string in file

I started working on my game few days ago. I made system for loading(on start) and saving(on exit). On first loging in everything is fine but on second error(NameError: name 'strange' is not defined) showed up. Can someone help me to solve it please? I know that problem is in finding name in file becouse I tryed to put else statment after everything under
elif ab.read().find(a) != -1:
and that what I put under else worked but it was just print so other required didn't worked under else.
Here's my program:
import sys, random, time, re
print("Welcome to game Special Travel Adventure!")
def con(a, b):
return a.lower() == b.lower()
a = str(input("If you want to continue with game type your name here: "))
ab = open("STAPlayers.txt", "r+")
if ab.read().find(a) == -1:
if ab.read() == "":
ac = "Name:" + a + ":Strange:" + "0" + ":Courage:" + "0" + ":Skills:" + "0" + ":Money:" + "0" + ":Level:" + "0" + ":Deaths:" + "0"
ab.write(ac)
strange = 0
courage = 0
skills = 0
money = 0
level = 0
deaths = 0
else:
ac = "\nName:" + a + ":Strange:" + "0" + ":Courage:" + "0" + ":Skills:" + "0" + ":Money:" + "0" + ":Level:" + "0" + ":Deaths:" + "0"
ab.write(ac)
strange = 0
courage = 0
skills = 0
money = 0
level = 0
deaths = 0
elif ab.read().find(a) != -1:
readdd = ab.readlines()
for line in readdd:
if line.find(a) != -1:
zm = line.split(":")
zm.remove("Name")
zm.remove("Strange")
zm.remove("Courage")
zm.remove("Skills")
zm.remove("Money")
zm.remove("Level")
zm.remove("Deaths")
strange = int(zm[1])
courage = int(zm[2])
skills = int(zm[3])
money = int(zm[4])
level = int(zm[5])
deaths = int(zm[6])
ab.close()
def levelc():
if courage and strange and skills == 1:
level += 1
return True
if courage and strange and skills == 2:
level += 1
return True
if courage and strange and skills == 3:
level += 1
return True
if courage and strange and skills == 4:
level += 1
return True
if courage and strange and skills == 5:
level += 1
return True
else:
return False
b = input("Start Menu\nSelect: Start, Upgrades, Exit. ")
while b != "dont save":
if con(b, "Exit"):
aj = open("STAPlayers.txt", "r")
lines = aj.readlines()
aj.close()
aj = open("STAPlayers.txt", "w")
jmj = "Name:" + a + ":Strange:" + str(strange) + ":Courage:" + str(courage) + ":Skills:" + str(skills) + ":Money:" + str(money) + ":Level:" + str(level) + ":Deaths:" + str(deaths)
for linee in lines:
if str(a) not in linee:
aj.write(linee)
elif str(a) in linee:
jmjm = jmj + "\n"
aj.write(jmjm)
aj.close()
sys.exit()
break
I know that problem is in finding name in file becouse I tryed to put else statment after everything under
elif ab.read().find(a) != -1:
and that what I put under else worked but it was just print so other required didn't worked under else. Please help.
Your problem is you are trying to read the file twice. You can only read a file once (without resetting to the beginning of the file). Try this:
ab_read = ab.read()
if ab_read.find(a) == -1:
...
elif ab_read.find(a) != -1:
...
Although, you might be better off using if a in ab_read: and then an else. Also, you might consider more meaningful variable names than ab or a.
To answer the next question you will have: your call to readdd = ab.readlines() will fail for the same reason multiple calls to ab.read() fail. You can get the same information with readdd = ab_read.splitlines()
In general, you are reading files way too often. Reading from files is a slow and resource consuming operation in any language. You chould consider reading the same file multiple times a very poor programming practice. You should refactor your code to only read each file once, and that should fix most of your issues.

Categories

Resources