I know I, for one, have troubles reading nested lists. Even for things like
[3, [7, None, [2, None, None]], [5, None, None]]
I have issues. But if you hand me something like
[1, [[2], 3, [4, 5], [6, [7, []], 8], 9], [10, [11]]]
I start to lose track of which way is up. I've looked around online for a way to make them more readable, but gave up and decided to make my own.
After goofing around in a text editor for a bit, I decided on a layout that looked fairly good and made all the nests readable:
[1, ************************************, **********]
[***, 3, ******, ***************, 9] [10, ****]
[2] [4, 5] [6, *******, 8] [11]
[7, **]
[]
With this exploded view, you can see how many elements are at each level and what they consist of, which makes a lot more sense! Then the question was...how should this be coded?
There were two options, assuming we should use a list of strings as the end result: either we find the maximum depth of the nested list and create that many strings before starting, or we add a new string every time we reach a new depth. We'll assume that the nested list we're given doesn't contain strings, because
- all the binary trees we deal with don't.
- triple-quoted strings would be a major hassle to deal with.
Firstly, we should find out the maximum depth of the nested list. While the program can be written without this, I was surprised to see it was consistently slower. A surprisingly elegant recursive version could be written as
def depth(x) -> int:
"""Returns the maximum depth of a nested list recursively."""
depths = []
for each in x:
if isinstance(each, list):
depths.append(depth(each))
if depths:
return max(depths) + 1
return 1
Now that we know the list's maximum depth, we can apply our knowledge.
def nest_writer(x: list, spacer: str="*") -> list:
"""Writes a nested list into a list of strings."""
str_list = ["" for y in range(depth(x))]
x = str(x)
current_line = -1 # the line we care about at any time
for char in x:
if char == "[":
current_line += 1
for each in range(len(str_list)):
if each < current_line:
str_list[each] += spacer
elif each == current_line:
str_list[each] += char
else:
str_list[each] += " "
if char == "]":
current_line -= 1
return str_list
For each character, it first checks if it's a left bracket, incrementing current_line if so. Then it uses the value of current_line as an index for str_list, so every line above it has spacer added to it, every line below it has " " added to it, and the string with index current_line has the character added to it.
As the coup de grĂ¢ce, we'll define two more functions:
def read_lines(x: list) -> None:
"""Prints each line in x."""
for each in x:
print(each)
def list_reader(x: list, spacer: str="*") -> None:
"""Calls all the relevant functions to print the list."""
read_lines(nest_writer(x, spacer))
Now, let's try it out:
>>> list_reader([1, [[2], 3, [4, 5], [6, [7, []], 8], 9], [10, [11]]])
[1, ************************************, **********]
[***, 3, ******, ***************, 9] [10, ****]
[2] [4, 5] [6, *******, 8] [11]
[7, **]
[]
You can try it out with different things, but ta-da! Now you can visualize nested lists a lot better!
-----------------------------------------------------------------------------------------------------------
Edit: The funny thing is, I ended up writing this before I knew what assignment 2 was, then ended up tweaking this to help me.
Also, I have no idea why the font broke for this entry in particular. I...think it's fixed?