Is there an elegant way to print this string? - string

Whilst trying to create a piece of code in order to prove the Collatz Conjecture, I tried to, in a single line, format all of the results (as can be seen in the last line of the script). Even though the output looks as desired, the last line in particular is over complex and long. I was wondering if someone could write the last couple lines together and better. Thanks!
PD: The last line of the script prints each value of the history = [] list and then adds an index to it. The output of 10, in turn, looks like this:
$python collatz.py
In: 10
0 | 10
1 | 5
2 | 16
3 | 8
4 | 4
5 | 2
6 | 1
Done!
Here's my code: (The code has now been edited based on the answers :))
#!/usr/bin/env python3
import time
def formulate(number):
history = []
counter = 0
while True:
history.append(number)
if number == 1:
break
if number % 2 == 0:
number = number // 2
else:
number = number * 3 + 1
counter += 1
return counter, history
counter, history = formulate(int(input("In: ")))
for idx, x in enumerate(history):
print("{} | {}".format(idx, x))
print('Done!')

I would just keep it simple and perform one print per output line:
counter, history = formulate(int(input("In: ")))
for idx, x in enumerate(history):
print("{} | {}".format(idx, x))
print('Done!')

Related

Horizotal print of a complex string block

Once again I'm asking for you advice. I'm trying to print a complex string block, it should look like this:
32 1 9999 523
+ 8 - 3801 + 9999 - 49
---- ------ ------ -----
40 -3800 19998 474
I wrote the function arrange_printer() for the characters arrangement in the correct format that could be reutilized for printing the list. This is how my code looks by now:
import operator
import sys
def arithmetic_arranger(problems, boolean: bool):
arranged_problems = []
if len(problems) <= 5:
for num in range(len(problems)):
arranged_problems += arrange_printer(problems[num], boolean)
else:
sys.exit("Error: Too many problems")
return print(*arranged_problems, end=' ')
def arrange_printer(oper: str, boolean: bool):
oper = oper.split()
ops = {"+": operator.add, "-": operator.sub}
a = int(oper[0])
b = int(oper[2])
if len(oper[0]) > len(oper[2]):
size = len(oper[0])
elif len(oper[0]) < len(oper[2]):
size = len(oper[2])
else:
size = len(oper[0])
line = '------'
ope = ' %*i\n%s %*i\n%s' % (size,a,oper[1],size,b,'------'[0:size+2])
try:
res = ops[oper[1]](a,b)
except:
sys.exit("Error: Operator must be '+' or '-'.")
if boolean == True:
ope = '%s\n%*i' % (ope,size+2, res)
return ope
arithmetic_arranger(['20 + 300', '1563 - 465 '], True)
#arrange_printer(' 20 + 334 ', True)
Sadly, I'm getting this format:
2 0
+ 3 0 0
- - - - -
3 2 0 1 5 6 3
- 4 6 5
- - - - - -
1 0 9 8
If you try printing the return of arrange_printer() as in the last commented line the format is the desired.
Any suggestion for improving my code or adopt good coding practices are well received, I'm starting to get a feel for programming in Python.
Thank you by your help!
The first problem I see is that you use += to add an item to the arranged_problems list. Strings are iterable. somelist += someiterable iterates over the someiterable, and appends each element to somelist. To append, use somelist.append()
Now once you fix this, it still won't work like you expect it to, because print() works by printing what you give it at the location of the cursor. Once you're on a new line, you can't go back to a previous line, because your cursor is already on the new line. Anything you print after that will go to the new line at the location of the cursor, so you need to arrange multiple problems such that their first lines all print first, then their second lines, and so on. Just fixing append(), you'd get this output:
20
+ 300
-----
320 1563
- 465
------
1098
You get a string with \n denoting the start of the new line from each call to arrange_printer(). You can split this output into lines, and then process each row separately.
For example:
def arithmetic_arranger(problems, boolean:bool):
arranged_problems = []
if len(problems) > 5:
print("Too many problems")
return
for problem in problems:
# Arrange and split into individual lines
lines = arrange_printer(problem, boolean).split('\n')
# Append the list of lines to our main list
arranged_problems.append(lines)
# Now, arranged_problems contains one list for each problem.
# Each list contains individual lines we want to print
# Use zip() to iterate over all the lists inside arranged_problems simultaneously
for problems_lines in zip(*arranged_problems):
# problems_lines is e.g.
# (' 20', ' 1563')
# ('+ 300', '- 465') etc
# Unpack this tuple and print it, separated by spaces.
print(*problems_lines, sep=" ")
Which gives the output:
20 1563
+ 300 - 465
----- ------
320 1098
If you expect each problem to have a different number of lines, then you can use the itertools.zip_longest() function instead of zip()
To collect all my other comments in one place:
return print(...) is pretty useless. print() doesn't return anything. return print(...) will always cause your function to return None.
Instead of iterating over range(len(problems)) and accessing problems[num], just do for problem in problems and then use problem instead of problems[num]
Debugging is an important skill, and the sooner into your programming career you learn it, the better off you will be.
Stepping through your program with a debugger allows you to see how each statement affects your program and is an invaluable debugging tool

Just started python and working through Automate The Boring Stuff with Python. Any recommendations on cleaning my code?

New here at stackoverflow. I'm learning python right now and picked up the book Automate the Boring Stuff with Python. Need some recommendations or tips on how to clean up my code. Here is one of the small projects from the book:
Write a function named collatz() that has one parameter named number. If number is even, then collatz() should print number // 2 and return this value. If number is odd, then collatz() should print and return 3 * number + 1. Then write a program that lets the user type in an integer and that keeps calling collatz() on that number until the function returns the value 1.
The output of this program could look something like this:
Enter number:
3
10
5
16
8
4
2
1
Here's the code I came up with. Any recommendations on cleaning up the code or is this good enough? Thank you all!
def collatz(number):
if number % 2 == 0: # Even numbers
print(number // 2)
return number // 2
elif number % 2 == 1: # Odd numbers
result = 3 * number + 1
print(result)
return result
while True: # Made a loop until a number is entered
try:
n = input("Enter a random number: ")
while n != 1:
n = collatz(int(n))
break
except ValueError:
print("Enter numbers only.")
Use else in the place of elif , it will give same reasult.
Optimized for readability and usage, not on performance.
def collatz(number):
print(n)
return number // 2 if number % 2 == 0 else 3 * number + 1
while True: # Made a loop until a number is entered
try:
n = input("Enter a random number: ")
while n != 1: n = collatz(int(n))
break
except ValueError: print("Enter numbers only.")

Difference resulting from position of print in loops

First question here.
I am picking up Python and have a question that may be quite basic.
I am trying to create this pattern with a nested loop:
x
x
x
xxx
With the code:
numbers = [1,1,1,3]
for count_x in numbers:
output = ""
for count in range(count_x):
output +=x
print(output)
My question is - why does my output change when I move the position of print(output).
The code above works but when I align print(output) with the for count_x in numbers:, I only get "xxx", when I align print(output) to output +=x, I get the following:
x
x
x
x
xx
xxx
which is very odd because there are only 4 items in the list and it shows me 6 lines of output.
Could someone please help? Really puzzled. Thank yall very much.
There's a difference between these two bits of code (fixing the x/"x" problem along the way - your code won't actually work as is unless you have a variable x):
# First:
numbers = [1,1,1,3]
for count_x in numbers:
output = ""
for count in range(count_x):
output += "x"
print(output)
# Second:
numbers = [1,1,1,3]
for count_x in numbers:
output = ""
for count in range(count_x):
output += "x"
print(output)
In the second, the print is done inside the loop that creates the string, meaning you print it out multiple times while building it. That's where your final three lines come from: *, ** and ***. This doesn't matter for all the previous lines since there's no functional difference. Printing a one character string at the end or after adding each of the one characters has the same effect.
In the first, you only print the string after fully constructing it.
You can see this effect by using a slightly modified version that outputs different things for each outer loop:
numbers = [1,1,1,3]
x = 1
for count_x in numbers:
output = ""
for count in range(count_x):
output += str(x)
print(output)
x += 1
This shows that the final three lines are part of a single string construction (comments added):
1 \
2 >- | Each is one outer/inner loop.
3 /
4 \ | A single outer loop, printing
44 >- | string under construction for
444 / | each inner loop.
In any case, there are better ways to do what you want, such as:
numbers = [1,1,1,3]
for count_x in numbers:
print('x' * count_x)
You could probably also do it on one line with a list comprehension but that's probably overkill.

Return statement inside for loop with range not working

I'm trying to work on a problem based on python as:
`Given an integer,print the following values for each integer from 1 to n :
Decimal
Octal
Hexadecimal (capitalized)
Binary`
What I did something like:
def print_format(number):
for i in range(number+1):
decimal=str(i)
binary=str(bin(i))
octa=str(oct(i))
hexagonal=str(hex(i))
return (decimal+' '+octa[2:]+' '+hexagonal[2:].upper()+' '+binary[2:])
print_format(5)
'5 5 5 101'
code returns only last set of value. But, what I'm expecting as,
0 0 0 0
1 1 1 1
2 2 2 10
3 3 3 11
4 4 4 100
5 5 5 101
Part of the code as with print statement works perfectly fine.
def print_format(number):
for i in range(number+1):
decimal=str(i)
binary=str(bin(i))
octa=str(oct(i))
hexagonal=str(hex(i))
print (decimal+' '+octa[2:]+' '+hexagonal[2:].upper()+' '+binary[2:])
Can anyone please explain what I did wrong while using return statement?
In your current attempt, you are looping through your input without doing anything. Return only sees the local variables after the last iteration and returns them. What you want is a generator:
def print_format(number):
for i in range(number+1):
decimal=str(i)
binary=str(bin(i))
octa=str(oct(i))
hexagonal=str(hex(i))
yield (decimal+' '+octa[2:]+' '+hexagonal[2:].upper()+' '+binary[2:])
mygen = print_format(5)
for i in mygen:
print(i)
This should print your desired output.
Just save all strings in a variable spliting with \n and return it.
def print_format(number):
result = ''
for i in range(number+1):
decimal=str(i)
binary=str(bin(i))
octa=str(oct(i))
hexagonal=str(hex(i))
result += decimal+' '+octa[2:]+' '+hexagonal[2:].upper()+' '+binary[2:] + '\n'
return result
print(print_format(5))

How to do a backspace in python

I'm trying to figure out how to print a one line string while using a for loop. If there are other ways that you know of, I would appreciate the help. Thank you. Also, try edit off my code!
times = int(input("Enter a number: "))
print(times)
a = 0
for i in range(times+1):
print("*"*i)
a += i
print("Total stars: ")
print(a)
print("Equation: ")
for e in range(1,times+1):
print(e)
if e != times:
print("+")
else:
pass
Out:
Enter a number: 5
*
**
***
****
*****
Equation:
1
+
2
+
3
+
4
+
5
How do I make the equation in just one single line like this:
1+2+3+4+5
I don't think you can do a "backspace" after you've printed. At least erasing from the terminal isn't going to be done very easily. But you can build the string before you print it:
times = int(input("Enter a number: "))
print(times)
a = 0
for i in range(times+1):
print("*"*i)
a += i
print("Total stars: ")
print(a)
print("Equation: ")
equation_string = ""
for e in range(1,times+1):
equation_string += str(e)
if e != times:
equation_string += "+"
else:
pass
print(equation_string)
Basically, what happens is you store the temporary equation in equation_str so it's built like this:
1
1+
1+2
1+2+
...
And then you print equation_str once it's completely built. The output of the modified program is this
Enter a number: 5
5
*
**
***
****
*****
Total stars:
15
Equation:
1+2+3+4+5
Feel free to post a comment if anything is unclear.
Instead of your original for loop to print each number, try this:
output = '+'.join([str(i) for i in range(1, times + 1)])
print(output)
Explanation:
[str(i) for i in range(1, times + 1)] is a list comprehension that returns a list of all your numbers, converted to strings so that we can print them.
'+'.join(...) joins each element of your list, with a + in between each element.
Alternatively:
If you want a simple modification to your original code, you can simply suppress the newline from each print statement with the keyword paramater end, and set this to an empty string:
print(e, end='')
(Note that I am addressed the implied question, not the 'how do I do a backspace' question)
Too long for a comment, so I will post here.
The formatting options of python can come into good use, if you have a sequence you wish to format and print.
Consider the following...
>>> num = 5 # number of numbers to generate
>>> n = num-1 # one less used in generating format string
>>> times = [i for i in range(1,num+1)] # generate your numbers
>>> ("{}+"*n + "{}=").format(*times) # format your outputs
'1+2+3+4+5='
So although this doesn't answer your question, you can see that list comprehensions can be brought into play to generate your list of values, which can then be used in the format generation. The format string can also be produced with a l.c. but it gets pretty messy when you want to incorporate string elements like the + and = as shown in the above example.
I think you are looking for the end parameter for the print function - i.e. print(e, end='') which prints each value of e as it arrives followed by no space or newline.

Resources