Python3: Midnumeral Strings to Rightnumeral Strings - python-3.x

My attempt:
m = str(input())
# m = list(m)
w = ""
r = ""
num = [int(i) for i in m.split() if i.isdigit()]
for c in range(0,len(m)-1):
if m[len(m)-1]:
for num in w:
if num.isnumeric():
w.remove(num)
num.join(r)
elif m[c].isalpha():
r.join(m[c])
elif m[c].isnumeric() and (len(w)==0 or w[c]=='('):
w.join(m[c])
elif len(num)!=0:
try:
if int(m[c])>int(w[c]):
w.join(m[c])
else:
n = [int(i) for i in w.split() if i.isdigit()]
if len(n)!=0:
r = ''.join([str(elem) for elem in w])
except:
continue
elif m[c]==')':
for number in m:
if number.isnumeric():
w+=number
elif number=='(':
m.remove('(')
m.remove(')')
break
elif (w[c]=='(') or ((int(w[c]))<(int(m[c]))) or (w=""): # In this statement it gives an invalid syntax error
w = m[c]
else:
break
print(r)
I would like to know why get a invalid syntax at line 34 (elif statement).
Also, would like to know if there is any better way to solve this question.
Thanks
QUESTION:
Midnumeral Strings to Rightnumeral Strings
Midnumeral string is a string which contains one number in between letters. For example, a1b2c is a midnumeral string. A midnumeral strings may contain letters a to z, numbers 1 to 9 and both left and the right parentheses.
Rightnumeral string is a string which can be formed from midnumeral string:
(i) All the characters except left and right parenthesis in the midnumeral string is present in rightnumeral string.
(ii) A number ‘n’ which is between letters ‘a’ anb ‘b’ in the midnumeral string will appear in the right hand of side of the letters ‘a’ and ‘b’ in rightnumeral string in a particular fashion.
(iii)During the process of conversion of a midnumeral string to a rightnumeral string, a waiting stream is used.
Given a midnumeral string ‘m’ it can be converted to a rightnumeral string ‘r’ as follows:
i. Process character by character of midnumeral string ‘m’.
ii. When a letter is seen add it to rightnumeral string ‘r’.
iii. When a number ‘n’ is seen and waiting stream is empty or the recently added character to waiting stream is left parenthesis then add ‘n’ to waiting stream
iv. If a number ‘n’ seen and there are some numbers in the waiting stream then, If the recently added numeral in waiting stream is less than ‘n’ then add ‘n’ to waiting stream, Otherwise process waiting stream from most recent to least recent:-Remove number from waiting stream and add it to ‘r’
Repeat part (i), until the recent value available in waiting stream is less than ‘n’ or the recent character the waiting stream is left parenthesis or the waiting stream becomes empty
v. Add ‘n’ to the waiting stream
vi. If a left parenthesis is seen then add it to waiting stream
vii. When right parenthesis is seen then remove all numbers from most recent to least recent from waiting stream and add it to r till left parenthesis is seen. Discard left and right parenthesis
viii. When the end of the midnumeral ‘m’ string is reached remove all numerals from most recent to least recent from waiting stream and add it to right numeral string ‘r’
For example,
a1b2c will be abc21
a2b1c will be ab2c1
(a1b)2c will be ab1c2
a1b4(c5d2e)7f will be abcd5e2f741
Note: All the characters in the midnumeral will be distinct and only valid input is given
Input Format
First line contains the midnumeral string, m
Output Format
First line should contain the rightnumeral string, r

.1 The Syntax error
It is simple a missing '=' sign, so change it to this
elif (w[c]=='(') or ((int(w[c]))<(int(m[c]))) or (w==""):
w = m[c]
else:
break
.2 Code improvements & Best practices
I suggest you for this to post the answer on Code Review as is meant for this. There are quite few errors and not best practices follow.
For instance, this error block:
try:
if int(m[c])>int(w[c]):
w.join(m[c])
else:
n = [int(i) for i in w.split() if i.isdigit()]
if len(n)!=0:
r = ''.join([str(elem) for elem in w])
except:
continue
The except is catching too many types of error, better like:
try:
if int(m[c])>int(w[c]):
w.join(m[c])
else:
n = [int(i) for i in w.split() if i.isdigit()]
if len(n)!=0:
r = ''.join([str(elem) for elem in w])
except ValueError:
continue
Have a look at Should I always specify an exception type in except statements? for more information about this. Also there is too much 'logic' in that try block. but for simplicity that is ok.
EDIT
Consideration
Note that I used different combination of sets ( accepted_numbers, accepted_chars, left_parenthesis, right_parenthesis) instead of list of other thing, this is because set are super fast when used for Looks up, more info --> Time complexity of python set operations?. And also is very clear what you are trying to use them for.
The Variable r, is way better decision to use it during the Algorithm as a List and Then turn it as a String, that's because is just easier to manipulate.
I don't even used 1 try except block, this is because, is was clear what to do, I mean we knew it ahead, so you can just handle it with a if else block, apart of what I stated Above regarding the Try blocks, another important aspect is that the slow down the code if finds and exception (in this code it won't even matter) look here try-except vs If in Python
The Algorithm problem that you posted uses and demands a Stack as a Collection, you can see it implemented in the process_waiting_stream() function
in a LIFO (last in, first out) fashion.
process_waiting_stream()
Answer Testing one case
m = '(a1b)2c' # test string Added on the top as requested in the exercise
import string # If you don't want to import string, just hard code the letters
m = m
r = []
# ----- CONSTANTS
accepted_numbers = {str(i) for i in range(1, 10)} # 1 to 9
accepted_chars = {*string.ascii_lowercase}
left_parenthesis = '('
right_parenthesis = ')'
parenthesis = {left_parenthesis, right_parenthesis}
midnumeral_string = accepted_numbers | accepted_chars | parenthesis
# ---- NOTE: Run a quick check is Input is Correct |!(a1b)2c or (a1b)0c is not good
for char in m:
if char in midnumeral_string:
pass
else:
print('Input String is not a Midnumeral string')
continue
# ---------------------------
WAITING_STREAM = []
r = [] # Use a list, not a string, easier to work with :)
def process_waiting_stream():
global WAITING_STREAM
if WAITING_STREAM:
WAITING_STREAM.reverse() # LIFO from most recent to least recent
while True:
if not WAITING_STREAM:
break
item = WAITING_STREAM[0]
if item == left_parenthesis:
break
elif item in accepted_numbers:
item = WAITING_STREAM.pop(0)
r.append(item)
WAITING_STREAM.reverse()
def checK_stream(): # NOTE: We check this in case there is only '(' and not a num
return any([item in accepted_numbers for item in WAITING_STREAM])
for char in m:
if WAITING_STREAM:
recently_added = WAITING_STREAM[-1]
if char in accepted_chars:
r.append(char)
elif char in accepted_numbers:
if not WAITING_STREAM or recently_added == left_parenthesis:
WAITING_STREAM.append(char)
elif checK_stream():
if (recently_added not in parenthesis
and recently_added not in accepted_chars) and int(recently_added) < int(char):
WAITING_STREAM.append(char)
else:
process_waiting_stream()
WAITING_STREAM.append(char)
else:
WAITING_STREAM.append(char)
elif char == left_parenthesis:
WAITING_STREAM.append(char)
elif char == right_parenthesis:
process_waiting_stream()
for item in WAITING_STREAM:
if item == left_parenthesis:
left_para = WAITING_STREAM.index(left_parenthesis)
WAITING_STREAM.pop(left_para)
break
process_waiting_stream()
r = ''.join(r)
print(r)
Answer 2
Testing All cases
import string # If you don't want to import string, just hard code the letters
def rightnumeral_string(m):
# ----- CONSTANTS
accepted_numbers = {str(i) for i in range(1, 10)} # 1 to 9
accepted_chars = {*string.ascii_lowercase}
left_parenthesis = '('
right_parenthesis = ')'
parenthesis = {left_parenthesis, right_parenthesis}
midnumeral_string = accepted_numbers | accepted_chars | parenthesis
# ---- NOTE: Run a quick check is Input is Correct |!(a1b)2c or (a1b)0c is not good
for char in m:
if char in midnumeral_string:
pass
else:
print('Input String is not a Midnumeral string')
continue
# ---------------------------
WAITING_STREAM = []
r = [] # Use a list, not a string, easier to work with :)
def process_waiting_stream():
nonlocal WAITING_STREAM
if WAITING_STREAM:
WAITING_STREAM.reverse() # LIFO from most recent to least recent
while True:
if not WAITING_STREAM:
break
item = WAITING_STREAM[0]
if item == left_parenthesis:
break
elif item in accepted_numbers:
item = WAITING_STREAM.pop(0)
r.append(item)
WAITING_STREAM.reverse()
def checK_stream(): # NOTE: We check this in case there is only '(' and not a num
return any([item in accepted_numbers for item in WAITING_STREAM])
# ------ Iterate Through each Character (number included) in the string
for char in m:
if WAITING_STREAM:
recently_added = WAITING_STREAM[-1]
if char in accepted_chars:
r.append(char)
elif char in accepted_numbers:
if not WAITING_STREAM or recently_added == left_parenthesis:
WAITING_STREAM.append(char)
elif checK_stream(): # Check if there are currently nums in stream
if (recently_added not in parenthesis
and recently_added not in accepted_chars) and int(recently_added) < int(char):
WAITING_STREAM.append(char)
else:
process_waiting_stream()
WAITING_STREAM.append(char)
else:
WAITING_STREAM.append(char)
elif char == left_parenthesis:
WAITING_STREAM.append(char)
elif char == right_parenthesis:
process_waiting_stream()
# Last Step is to Remove left_parenthesis
for item in WAITING_STREAM:
if item == left_parenthesis:
left_para = WAITING_STREAM.index(left_parenthesis)
WAITING_STREAM.pop(left_para)
break
process_waiting_stream()
r = ''.join(r)
return r
print(rightnumeral_string('a1b2c'))
print(rightnumeral_string('a2b1c'))
print(rightnumeral_string('(a1b)2c'))
print(rightnumeral_string('a1b4(c5d2e)7f'))
# Checking if the Answer Are correct
assert rightnumeral_string('a1b2c') == 'abc21'
assert rightnumeral_string('a2b1c') == 'ab2c1'
assert rightnumeral_string('(a1b)2c') == 'ab1c2'
assert rightnumeral_string('a1b4(c5d2e)7f') == 'abcd5e2f741'
Documentation
string
Iterating each character in a string using Python
What is the use of “assert” in Python?

Related

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)

python function call not printing as expected

So I have 2 functions - displayHand(hand) and calculateHandlen(hand)
def displayHand(hand):
"""
Displays the letters currently in the hand.
For example:
>>> displayHand({'a':1, 'x':2, 'l':3, 'e':1})
Should print out something like:
a x x l l l e
The order of the letters is unimportant.
hand: dictionary (string -> int)
"""
for letter in hand.keys():
for j in range(hand[letter]):
print(letter,end=" ")
print()
def calculateHandlen(hand):
"""
Returns the length (number of letters) in the current hand.
hand: dictionary (string-> int)
returns: integer
"""
handLen = 0
for i in hand:
handLen = handLen + hand.get(i,0)
return handLen
There's a loop in another function that is dependent on the above functions -
def playHand(hand, wordList, n):
"""
hand = dictionary
wordList = list of valid words
n = an integer passed while function call
"""
totalscore = 0
while(calculateHandlen(hand)>0):
print("Current Hand: " +str(displayHand(hand)))
newWord = input('Enter word, or a "." to indicate that you are finished: ')
Function call for playHand() is as follows:
wordList = loadWords() #loadWords could be a list of words
playHand({'n':1, 'e':1, 't':1, 'a':1, 'r':1, 'i':2}, wordList, 7)
I'm expecting the output to be:
Current Hand: n e t a r i i
Enter word, or a "." to indicate that you are finished:
However, it displays the following:
n e t a r i i
Current Hand: None
Enter word, or a "." to indicate that you are finished:
Don't know where I'm going wrong.
Note: I'm not allowed to make any changes to the first 2 functions.
displayHand() doesn't return anything, so the default None is returned. When you call it, it prints the output directly.
If you are not allowed to alter displayHand(), you need to print your label first, then call the function:
print("Current Hand: ", end='')
displayHand(hand)
The end='' removes the newline print() would normally write.

Statement has no effect(Python)

program to get a string from a given string where all occurrences of its first char have been changed to '$', except the first char itself.
Sample String : 'restart'
Expected Result : 'resta$t'
here's my code
def change(string):
string_len = len(string)
t = string[0]
for each in range(1, string_len):
if each is t:
each == '$'
else:
continue
return string
print(change("restart"))
output
restart
i'using Pycharm. Line no 6 (each == '$')says this statement has no effect. i don't want to use replace method. just want to know what is the problem.
Your code commented:
def change(string):
string_len = len(string)
t = string[0]
for each in range(1, string_len):
if each is t: # To compara strings you should use the == operator not the 'is' operator.
each == '$' # This will not have any effect because is a temporal variable visible just inside the 'for' loop and you are not using it.
else:
continue
return string
print(change("restart"))
A solution could be:
def change(s):
result = ''
for character in s:
result += '$' if character == s[0] else character
return result
print(change('restart'))
Python strings are immutable objects, so you can't do 'aaa'[1] = 'b' to get aba.
each is set to an integer and you are comparing it to a string.

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

Having trouble with str.find()

I'm trying to use the str.find() and it keeps raising an error, what am I doing wrong?
import codecs
def countLOC(inFile):
""" Receives a file and then returns the amount
of actual lines of code by not counting commented
or blank lines """
LOC = 0
for line in inFile:
if line.isspace():
continue
comment = line.find('#')
if comment > 0:
for letter in range(comment):
if not letter.whitespace:
LOC += 1
break
return LOC
if __name__ == "__main__":
while True:
file_loc = input("Enter the file name: ").strip()
try:
source = codecs.open(file_loc)
except:
print ("**Invalid filename**")
else:
break
LOC_count = countLOC(source)
print ("\nThere were {0} lines of code in {1}".format(LOC_count,source.name))
Error
File "C:\Users\Justen-san\Documents\Eclipse Workspace\countLOC\src\root\nested\linesOfCode.py", line 12, in countLOC
comment = line.find('#')
TypeError: expected an object with the buffer interface
Use the built-in function open() instead of codecs.open().
You're running afoul of the difference between non-Unicode (Python 3 bytes, Python 2 str) and Unicode (Python 3 str, Python 2 unicode) string types. Python 3 won't convert automatically between non-Unicode and Unicode like Python 2 will. Using codecs.open() without an encoding parameter returns an object which yields bytes when you read from it.
Also, your countLOC function won't work:
for letter in range(comment):
if not letter.whitespace:
LOC += 1
break
That for loop will iterate over the numbers from zero to one less than the position of '#' in the string (letter = 0, 1, 2...); whitespace isn't a method of integers, and even if it were, you're not calling it.
Also, you're never incrementing LOC if the line doesn't contain #.
A "fixed" but otherwise faithful (and inefficient) version of your countLOC:
def countLOC(inFile):
LOC = 0
for line in inFile:
if line.isspace():
continue
comment = line.find('#')
if comment > 0:
for letter in line[:comment]:
if not letter.isspace():
LOC += 1
break
else:
LOC += 1
return LOC
How I might write the function:
def count_LOC(in_file):
loc = 0
for line in in_file:
line = line.lstrip()
if len(line) > 0 and not line.startswith('#'):
loc += 1
return loc
Are you actually passing an open file to the function? Maybe try printing type(file) and type(line), as there's something fishy here -- with an open file as the argument, I just can't reproduce your problem! (There are other bugs in your code but none that would cause that exception). Oh btw, as best practice, DON'T use names of builtins, such as file, for your own purposes -- that causes incredible amounts of confusion!

Resources