1

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!

  • Inspect the contents of `arranged_problems` in the `arithmetic_arranger()` function. Does it look right? Suggestions for good coding practices: Give your variables names that make sense. Code that's easy to read is easy to debug. [Debug your code](//ericlippert.com/2014/03/05/how-to-debug-small-programs/)!! [Step through it using a debugger](//stackoverflow.com/q/25385173/843953) if you need to observe how each statement changes your variables. – Pranav Hosangadi Aug 13 '21 at 16:00
  • @Pranav It looks like every character is living its own live, separated from what should be its string. But executing `print(type(arrange_printer(' 20 + 334 ', True)))` returns a `str`. So, what I'm doing wrong? – Oscar Espinosa Aug 13 '21 at 16:10
  • 1
    Strings are iterable. `somelist += someiterable` iterates over the `someiterable`, and appends _each element_ to `somelist`. To append, use `somelist.append()`. – Pranav Hosangadi Aug 13 '21 at 16:16
  • Here's my thought process to arrive at that conclusion (if I didn't know what `+=` did): 1. `arrange_printer()` returns a string. 2. Hmm, but when I add that to my list `arranged_problems`, something goes wrong. 3. Let me see how I add it to the list. 4. Hmm, does `+=` really do what I want it to do? 5. Let me test it out. `l = []; l += "abc"; print(l)` gives `['a', 'b', 'c']`. 6. Aha! – Pranav Hosangadi Aug 13 '21 at 16:19
  • However, even once you fix this using `append()`, it [won't work like you expect it to](https://i.stack.imgur.com/56NVt.png), because you seem to have misunderstood how `print()` works. 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. – Pranav Hosangadi Aug 13 '21 at 16:19
  • 1
    Also note that `return print(...)` is pretty useless. `print()` doesn't return anything. `return print(...)` will always cause your function to return `None`. And instead of iterating over `range(len(problems))` and accessing `problems[i]`, just do `for problem in problems` and then use `problem` instead of `problems[i]` – Pranav Hosangadi Aug 13 '21 at 16:21
  • 1
    One hint I would give you is: 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. – Pranav Hosangadi Aug 13 '21 at 16:24
  • Thank you, I understood that part of my problem was `+=` instead of `append()`. As I see I should rearrange the string to 3 or 4 strings within the function `arrange_printer()`. – Oscar Espinosa Aug 13 '21 at 16:26

1 Answers1

2

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
Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70