Showing posts with label recursion. Show all posts
Showing posts with label recursion. Show all posts

Friday, 6 December 2013

Reading Nested Lists

     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?

Bonus Update: OOP(s) and Recursively Recursing Recursion

From http://news.sabay.com.kh/wp-content/uploads
/2013/04/ObjectOrientedProgramming.png
.
Yes, the asymmetry is bothering me too.
No, I don't know why it's like that.
Well, now I feel silly. After I made two entries talking about recursion, we have to do a bonus entry about object-oriented programming and, yes, you guessed correctly, recursion! Is it too late to rename this thing Recursion: The Desolation of SLOG? (I don't know why I made that joke: I haven't even seen the previous Hobbit movie yet.)

From http://caseelse.net/wp-content/uploads/2008/05/recursionagain.jpg.
I'll confess: I do like recursion jokes.
     Anyways, object-oriented programming, as the name implies, revolves around objects, and the classes used to create them. You can give objects both attributes and methods, whereas in languages like the original FORTRAN, they have no such thing. It's useful for computer science, as you can represent things and manipulate them far easier than trying to associate random variables for each thing you're trying to represent.

     Recursion is taking a complex problem and simplifying it by having subsequent iterations of the function apply to smaller sections of the original problem until they're at their base cases and therefore easier to solve. Without it, functions would be a nightmare of trying to get things done with while loops, if they were even possible to replicate at all.

Binary Trees: Not Quite Like Family Trees

From http://lcm.csa.iisc.ernet.in/dsa/img151.gif
     I can already tell now that I'm going to have issues recursing over binary trees, let alone remembering how to implement the three versions of even listing all their items.

     I don't know if I'm just so used to seeing flat lists that I'm simply not used to them yet, but they look so strange. Besides, it must be a nightmare if you have a sorted binary tree and decide to delete a node. How do you rearrange it so it stays ordered? I looked at the Wikipedia section on the topic, but it looks like the closer the node you want to delete is to the root, the better it would be to simply power off the computer and go watch a movie.

SLOG.__init__(self)

     For those who've looked at my SLOG before and found it blank, I'm sorry: I've started a lot of drafts (or just kicked an idea around in my head), but never actually finished or fully posted any yet, so all of these are going to look back-to-back.
-----------------------------------------------------------------------------------------------------------
Yes, Google, I did mean...wait a second.
     Whenever recursion gets applied to computer science, usually one of three things happen:
  1. Bad jokes abound.
  2. The answer seems simple. "So the only cases are...okay, that makes sense."
  3. The question may as well have been written in Wingdings. "So it has to either...but it can't...what?"
     When it's things like the total sum of a nested list, life is great. But when you get to cases with unusual objects, or multiple cases, it's difficult to even start: you have to figure out what variables the function will take, and how the function will return those values to another iteration of itself for more processing, and eventually you realize you just pulled your hair out in clumps and will probably need a wig. Also, that you've still failed to write anything down.

     One thing I've noticed is that I have a bad habit to try and figure out how the internal bits of the function work: from what I've seen, it's far better to consider what the possibilities are for it to continue recursing or to be an end case and be able to start returning things. Regardless, I'm still going to need a lot more work at it: this is definitely not something that comes naturally at all.