Monday, June 24, 2013

This is the 5th in a series of posts. If you want to go the the start, go here.

Functions are a lot of fun! While in essence, all they do is help you organize your program, their key contribution to programming is that they empower the programmer to think more abstractly about what they want to achieve. Instead of thinking strictly in terms of loops and ifs, functions let you think in terms of goals. Not only that, they enable the programmer to "divide and conquer" by breaking up the main goal into smaller sub-goals, and then put them together to achieve the desired end.

Sounds like fun, doesn't it? :)

For example, say you wanted to add together the maximum values of two lists.
If one list had the values [3, 6, 12, 3, 6] and another [9, 5, 2, 5, 3], then you would want to add together 12 and 9 to get 21.

By now, you should already be able to do that. In fact, I'm sure that loops and ifs are running through your mind at this very moment. Great! :) Because I want you to appreciate functions, I need you to suffer a little first:

Assignment 5.1: go head and write code that does just that: adds the maximum values of two lists.

Great! I'm guessing that you either did some loop within a loop, or two separate loops, one after the other. Either way, your solutions can be simplified, and thus made more elegant, with the use of functions. Check this out:

Can you figure out what it does by just looking at it? If you guessed that "find_max" is a function that calculates the maximum item in a list, then you guessed right! (Woohoo!) We then use this function twice, once per list that we want to find its max item. But let me go over the details with you:

Basically, the keyword def is Python's way of letting you define a function! Defining a function doesn't actually do anything! It just tells Python that there is a new function that can be used later on. I'll go into it soon, but for now, let's go over how a function is structured.

After the def, you give the name of the function, which like a variable, can be just about anything you feel like. To be useful, functions usually have inputs and outputs. In tech speak, the inputs are called "input parameters" or just plain "parameters" or even "arguments", and the outputs are called "return values" or sometimes "output parameters". Besides inputs and outputs, the function also does stuff. The stuff it does, in tech speak, is called the function's "body".

So in logical order, first the function takes its inputs, or parameters. Then, in its body, it does stuff (usually to the inputs), and finally, it returns whatever it wants as output to whomever used it. In tech-speak, using a function is called "calling" or "invoking" it. As I mentioned earlier, just defining a function is pretty useless, to actually get some work done, one needs to call the function. Thus in our example, our find_max function is called twice: once, with list1 as its parameter and assigning the return value to max1, and the second time with list2 as its parameter, assigning the return value to max2. Do you see how when calling a function, you place whatever input you want to give it in parenthesis?

Part of what makes functions so powerful is just this: while you write them only once, you can call them with different inputs and get different outputs! Behind the scenes, the way it works is basically like this:
When Python encounters the line: max1 = find_max(list1), it:

goes to the code where you defined find_max. If it can't find it, you'll get an error message (duh!) Most likely it won't be able to find it if you typed in its name incorrectly. Congratulations! :)

Python makes sure that you're calling it with the right amount of input parameters. Again, if you called it with the wrong number of inputs, you'll get an error!

It then proceeds to assign each of the caller's input parameters to the input function's input variables. This copies a reference of the caller's variables into the function's variables. In our case, there is only one input parameter, so it only needs to do this:mylist = list1

See how it assigned the caller's variable, in our case "list1", to "mylist"? Neat, huh?

Then it performs, or in tech-speak, "runs", the body. This works exactly the same as any code you have already written. Nothing new here!

Nothing new except the "return" command, which tells the function to exit with some value. In our case, it would normally be max_item (but it can also be None, more on this below).

It then assigns a reference to the return values to whatever variable the caller wants to, in our case, it would do something like this:max1 = max_item

Got it? Good! :) I gave you the details here just so you see that it's not really magic. There is a little more to it than I mentioned, and I'll mention some if it a bit later, but that's pretty much it! Nice, huh?

I want to tell you a bit more about the "return" statement. A return is a way to end the function, and return to the caller whatever output you want. Our function has two exit points: at one point it returns None as an output to the caller (which may cause problems for us later on, but I'll let you try to figure out why). And at another, it returns whatever is the value of max_value at the time.

This would be a good time for you to try and type in the code example above and try to run it! Pay attention to the tabs, because as we know by now, they're critical in Python. Notice the colon at the end of the def line, and that the entire function body is indented.

Assignment 5.2: type in the code above and play with it, change the values of list1 and list2 and see what happens.

BTW, after running your code, type this in the Python Shell:
>>> type(find_max)

I find it interesting that functions can be thought of as information as well. But different from numbers or lists because a function's information describes how to perform various actions!

In our example, the function max_one has one input parameter, and one output parameter. But this doesn't need to be the case. A function can have as many input and output parameters as you want. It can have zero or more input parameters, and zero or more output parameters. The way to have more than one parameter is to separate them with commas, like this: (try it!)

You can also have a function without any inputs or outputs: (try this too!)

And of course, you can mix and match however you like: (and this too!)

See?! That's a function with two input parameters, but only one return value. Really, it's that simple.

Sometimes, you may not care for the return value of a function. This may be the case if the body of the function is more important to you than its output. If you don't assign the return values to anything, Python just throws them away: (guess what? try this too)

Here we call do_stuff twice. The first time, we don't assign its return value (5) to anything, but we still get to see "amazing! 5" displayed on the screen. The second time, we assign the return value to z, and then print it to the screen. Try it!

Now it's your turn to write some code:

Assignment 5.3: write a function (call it "foo") that takes in two parameters, and returns the smaller of the two.

Were you able to figure it out? There are plenty of ways to write it, and of course, you can call the input parameters whatever you like, but it should look something like this:

Here's another one:

Assignment 5.4: write a function that takes in three parameters, and returns the smaller of the three.

And yet another:

Assignment 5.5: write a function (call it "find_min") that takes in a list, and instead of returning its smallest item, just prints it to the screen. That is, this function doesn't have any return values.

Assignment 5.6: now change the above function and have it actually return the smallest item, returning None if the list is empty.

Good.

Functions and references

Remember the tutorial on information, and how I went on and on about references? I mentioned that when we get to functions (as well as objects, later on), this gets important. Well, we're here! :)

Because in Python, input (as well as output) parameters are passed in by reference, it means that in a way, input parameters aren't strictly input parameters. They can be used to modify their inputs, and thus, in a way, be output parameters as well! Check this out:

What do you expect to see happen? Try it!
Do you see how the value of a was modified from within the function? This happens because listlist is referencing a. If you modify the parameters this way, they become both input and output parameters. Something to keep in mind.

One is the loneliest number...

Having one function is nice for simple things. But if you have more complicated things that you want to achieve, you may want to break up the task into two or more functions. Of course, each function can call any other function. Check out this little example:

Can you follow what happens? Here we defined two function,the first, find_min2, returns the minimum of two numbers. We then define find_min3 which usesfind_min2 in order to easily find the minimum of three numbers! Try typing it in to see how this works! The key point here is that within the body of a function, you can do whatever you want, including calling other function!

Assignment 5.7: write two functions, find_max2 and find_max3 in a similar manner to the above example.

When a function returns only one value, it's possible to nest them. Nesting functions is tech-speak for directly using the output value of one function as the input value of another. Check out how I can rewrite find_min3:

Can you see how now one function call is actually placed within the other? That's nesting for ya! First, Python runs find_min2(a, b). The reason it runs this inner call first is because it needs to in order to find the value of the first input parameter for the outer call! So basically, it figures out the minimum between a and b, and then the minimum between that minimum and c. To better see this, add some print statements to find_min2 to see exactly how it is being called:

Of course, we could have written find_min3 slightly differently, and it would still work correctly:

Do you see that the difference here is that it first find the minimum between b and c, and then that minimum and a? Play with it a bit until you get a good feel for it. :) It's a bit tricky, but very orderly all the same.

Assignment 5.8: modify the find_max3 that you previously wrote to work using a nesting.

Other people's functions

One of the most useful things you can keep in mind as a programmer is that when it comes to many problems, you're not the first to have encountered them! Besides unit-testing, knowing that you can use other people's code is what makes a programmer a professional! Now, I'm not recommending stealing other's code. There is a lot of code out there that is free and ready for everyone to use! In fact, the very Python interpreter & the IDLE editor that you're using thus far has been free, and it was useful, wasn't it? Same is true about other people's code.

Using others' time-tested, proven code is usually better than writing a buggy version of the same yourself.

Now, the biggest problem of using other's code is that there is so much of it to chose from! Really, it gets hard actually finding the code that you want to use, because it's buried in a heap of other code that you don't really want. But it's a good habit to get familiar with the available code base of whatever language you're using. As this tutorial develops, I'll slowly introduce you to what the Python community has to offer.

So after much ado, let me introduce you to Python's built-in functions. These are functions that were deemed soooooo useful for general programming that the language just comes ready with them! One such function that you're already familiar is "len", which returns the number of items in a list (or even the length of a string). But there are more, lots more. You can find them all here! Go ahead and take some time now to go over them. You don't need to memorize what all of them do, just get a feel for what's being offered. Some of the ones that I find most useful are:

len(), enumerate(), id()

range(): returns a list of numbers from 0 to whatever you want. In fact, it doesn't even need to start at 0. It's very useful in a for loop, say, if, you know you want the loop to run a specific number of times.

min() and max(): they can be used in various ways, such as: min(3, 5, 9), or with a list, such as min([3, 1, 3, 5, 3]). Neat, huh?

int(), float(), str(): int returns an integer, you can call it like this: int(3.9), int("45"). float() and str() do similar things, but return different types.

round(): similar to int(), but rounds to the nearest value. compare the results of int(3.9) and round(3.9)

One of the nicest things about Python is the interactive interpreter. It lets you try out new code quickly and easily to see what it does... You can do things like:

>>> range(10)

>>> range(5, 20)

>>> range(4, 20, 3)
>>> for a in range(20):
print a, a*a

>>> min(3, 6, 7, 2, 4, 7, 2, 4)

>>> round(2.9)

>>> int("37")

>>> str(38)

>>> float("28.9")

>>> float(23)

Neat, huh? You get all those goodies, and for absolutely free! So scan though those built-in functions, and play around with whichever seem interesting to you! :)

Functions can be very useful when dealing with such structured data. For instance, one can imagine a function that you can call, giving the people information as input, and it returning the first record it finds with a given name:

You can see that it takes in two input parameters: the people database, as well as a name that it uses as comparison. It also has one return value: the record found in the database (probably None if none found)...

Assignment 5.9: try and figure out how to write such a function! :)

If you couldn't figure out, no worries, here's one way to do this:

Do you see how that works? the names of the input parameters are completely arbitrary... You can change them from "ppl" and "name" to "a" and "b" and it would exactly the same (assuming you change the references to them in the function's body as well...

Assignment 5.10: rewrite the above function to work with input parameters "a" and "b", just because you can.

Now something similar:

Assignment 5.11: write a function, call it foofoo that takes in the people database as well as an age, and returns the first person that is at least that old... You can assume that today is 2013, or whatever year it is today... :)

But I can think of something even more useful than that! How about instead of returning the first person to match some criteria, make some function that returns a list of people that match the criteria! Can you think of how to do that? Remember from the previous lesson that you can append things to lists.........

Assignment 5.12: write a function, call it find_gender, that takes in the people database as well as a gender ('M' or 'F'), and returns all the people that match.

Were you able to figure it out? If not, I'll give you a little hint, ready? One way to do this is to start out with an empty list, then go through the input parameter that contains the people database, appending to that list all the items that match. Good luck!

BTW, when you call this find_gender() of yours, what do you get as a result? Interestingly enough, you get another people database, but smaller! Ones that contain only the people that match. For instance you can do something like this:

>>> males = find_gender(people, 'M')
>>> print males

You should see only guys here. Sad, but such is life. Then you can do something like this:
>>> blue = find_gender(males, 'F')
>>> print blue

What do you expect blue to contain? Notice that when you call it, you're passing to it not the people database, but the males database. Try it! Did what you get make sense?

While this example wasn't very fascinating, you can make things more complicated:

Assignment 5.13: write a function, call it find_older_than, that takes in the people database as well as an age, as returns a list that contains all the matching people.

Were you able to do it? Not hugely different than find_gender(), wasn't it? But now, you can mix and match them together! Say you wanted to find all the females that are older than 50. Can you figure out how to do that?

Assignment 5.14: take advantage of both find_gender() as well as find_older_than() that you wrote in order to find all females older than 50.

Pretty neat, huh?

Do you see where I'm going with this? You can make functions that solve a particular problem, and then put them together in order to solve more complicated problems. What makes this concept even more powerful is that this also enables you to unit test each individual function separately, make sure that it's working well, and then unit test the combination of the functions. But at that point, you already built up some confidence that at the very least the building blocks are solid, and you can concentrate on testing that their combination, or in tech speak, "integration", is correct.

Assignment 5.15: write a function that given a list of cars, returns the average number of miles on them.

Assignment 5.16: write a function that given a list of cars and a year, returns a list of all the cars that are that year or newer.

Of course, as you write these functions, you can change the cars list, that is, go ahead and add more cars to it! Here's a little challenge:

Assignment 5.17: write a function that given a list of cars, returns a list of all the available colors. Make sure that each color only appears once!

Assignment 5.18: write a function that given a list of cars, returns a list of all the available makes? Again, make sure that each make only appears once!

Did you notice that the above two assignments are rather similar? Can you think of some sort of "helper" function that you can write, that would simplify the above two solutions? Is there some sort of mundane, repetitive task that you're doing in both functions that you can sort of "take out" and put into a third? Then call it from the two assignments?

Well, did you come up with any ideas? Guess what?! I have one!! How about writing a function that appends an item into a list only if it's not in there already? Keep references in mind, and this becomes a lot simpler.

Assignment 5.19: write a function, call it append_unique, that appends an item to a given list only if it's not there already.

Assignment 5.20: modify the functions that you wrote in assignments 5.17 and 5.18 above to use the append_unique function that you just wrote! Neat, huh? That's breaking down a problem for ya!

Now, if you haven't already done so (to satisfy your curiosity, of course), put some of these functions together:

Assignment 5.21: write a function that given a list of cars, returns all the colors that are available for cars made on or after the year 2000.

Very nice! :)

A little Python treat ...Python has an elegant way to check to see if a list contains a particular item:
>>> x = [2, 5, 3]
>>> a = 7 in x
>>> b = 5 in x
>>> print a, b

Thus you can do something like this:

You like? You can use the same approach to check to see if a map contains a particular key:
>>> y = {"hi": 3, "there": 5}
>>> a= "hi" in y
>>> b = "what" in y
>>> c = 3 in y
>>> print a, b, c

Notice that c is False, because it's in there as a value, and not as a key.

Assignment 5.22: modify append_unique() to use the "in" keyword instead of the for loop that you probably wrote. :)

Functions as tools for unit testing

Functions are great for unit testing your code. One can write a simple function to help with testing like so:

>>> def TestSomething(someInput, expectedOutput): ...

You can then run it with many different inputs and expected outputs and make sure that what you want works well. A slightly better (more generic) way to do this is as follows:

Do you see how this lets you test a function (in this case, Max?) This is just one simple example, and you can definitely create more sophisticated testing tools, it all depends on your needs. :)

I bet you couldn't wait for some more Minesweeper! :)

Functions let us take our Minesweeper code to a whole new level! Let's start with the more straightforward things that you can do:

Can you write a function, call it IsCellLost(), that takes in a Minesweeper cell as an input parameter, and returns True if a player had clicked on this cell, and this cell has a mine?

How about a function, IsCellTowardsWin(), that returns True if the player had clicked on this cell, and it has no mine?

Of course, how about the function IsGameLost(), that takes a Minesweeper grid, and returns True if any of the cells containing a mine were clicked. Of course, you can use the function IsCellLost() to help you out.

And you saw this coming, didn't you: write a function, IsGameWon(), that takes a Minesweeper grid, and returns True if the game is won. That is, all the cells that don't have a mine have been clicked, and none of the ones that do have a mine have been clicked. If course, you can use the function IsCellTowardsWin() to help you out!

To help you out / debug your code, you can write helper functions, such as PrintCell() and PrintGrid() which prints a given cell / grid to the screen.

Of course, remember to unit test your code!!! :)

Now, for a little challenge: I want you to create a function, call it GenerateEmptyGrid(), that takes in the number of rows and columns and returns a new grid with no mines, and no cells have been flagged or clicked. For instance, calling:

>>> myGrid = GenerateEmptyGrid(4, 5)

Should return a new Minesweeper grid that has 4 rows and 5 columns. That, and nothing else. :) Remember to watch out for references here—you want to make sure that every cell in your grid is different from one another (that is, has a different ID from each other.) A little hint here: you will probably want to use Python's built-in function, range(), that I briefly mentioned a little earlier.

Now the fun part. Write another function, call it AddMinesToGrid(), that takes in a previously created grid, and a probability that any given cell inside it should contain a mine. It should then modify that grid by adding mines accordingly. Remember that variables are passed into functions by reference, it should help you out here. For instance, calling:

>>> AddMinesToGrid(myGrid, 0.3)

Should place a few mines randomly inside it, with a 30% chance of any given cell containing a mine. Thus, the closer the prob is to 0, one should expect fewer mines, and the closer the prob is to 1, one should expect to see more mines. BUT! You ask: how the heck do I do random stuff in Python? Ah-Ha! Don't worry, Python has a neat little library (more about libraries later) that let you do random stuff. And, it's really easy to use. All you have to do, is at the very start of your program file (before you define functions, and do stuff), have this line of code:

>>> import random

I'll go into what it does later, but basically, it tells Python that you want to use the "random" library.

After you do the import, you can generate random numbers anywhere in the code (including inside loop or functions or wherever) simply by calling this strange-looking function:

>>> x = random.random()

>>> print x

Basically, this tells Python that you want to call the "random" function inside the "random" library. Pretty straight forward, I would say.

Now, what does random.random() return? Incredibly enough, it returns a random number (sort of random, actually, but more on this later, but good enough for our purposes here). And amazingly enough, this number is between 0 and 1! Wow! Can you figure out how to use it in order to determine whether any cell in the given grid should be given a mine? Think about it. :)

After implementing AddMinesToGrid(), there are a couple of other functions that you may want to write:

FlagACell(): given a grid, and the coordinates of a particular cell within it, mark it as "flagged"

ClickOnACell(): mark a particular cell as clicked...

Whew! I hope you enjoyed this as much as I have.... :)

Have you noticed, btw, that now you have a lot of the tools needed to play a fake game of Minesweeper? That is, you can create an empty grid, populate it with random mines, and start clicking around, checking to see whether you won (or lost) the game? A few key functions are still missing, can you think of what they are? Don't write them just yet, just think about what additional functionality is missing.... Unless you really want to write them, of course... :)

We're done for now!

I hope you enjoyed this little introduction to functions. In the next lesson, I'll give you more practice with functions, but I'll also take you into the realm of the infinite....

Sunday, June 9, 2013

This is the 4th in a series of posts. If you want to go the the start, go here.

In the previous section, you got some experience with loops and conditionals. I can't overstate how important these concepts are to programming. Being what programming actually is, I want to spend some more time on these topics, mostly by giving you more practice with them.

You know what they say: practice makes better!

Time to get real: databases

Databases? Have you heard of them? No? Too bad. Yes? Great! They're basically a great way to store and retrieve a whole bunch of different information. So why are we talking about them here, and not in the section on information? Well, to really take advantage of the way that databases work, for loops and conditionals are essential. Let me explain:

Databases basically store lots of information, but in a structured way. How structured? Great question! They containdifferent tables, and each table contains a bunch of similar items. Sounds familiar? In Python, it can be implemented as a list of dictionaries. Say what? Yup, now is a good time to go back to that old lesson and brush up on dictionaries. But a list of dictionaries can be something like this:

>>> q = [ {"z": 3, "a": "hi"}, {3: "there", "what": "not"}]

Etc. The above is a mess, not very structured at all. In databases, all the dictionaries contain the same keys, and each key has a value of the same type (text, number, etc.) For instance, a table containing car information may look like this:

>>> cars = [

{"make": "toyota", "year": 1997, "color": "blue", "miles": 129732},

{"make": "ford", "year": 2003, "color": "green", "miles": 83832},

{"make": "toyota", "year": 2007, "color": "green", "miles": 27212}

]

or one containing people information may look like this:

>>> people = [

{"name": "Sally", "gender": "F", "birth_year": 1972, "height": 175},

{"name": "Ido", "gender": "M", "birth_year": 1923, "height": 143},

{"name": "Fred", "gender": "M", "birth_year": 2010, "height": 63},

{"name": "Molly", "gender": "F", "birth_year": 1948, "height": 163},

]

I purposely wrote these as text so you can copy them into your code and play around with them. Do you notice how they're structured? For instance, in cars, all the makes are text, while all the years are numbers. Also, in people, all the genders are "M" or "F", while all the heights are in numbers (and presumably in cm—if you're a crazy American, and you want the height to be in inches, well, I'll leave that for you as an exercise).

Do you remember how to access these dictionaries inside lists? If not, you should probably go over the relevant section in that post, but a quick recap:

Remember now? Great!

You're probably getting the feeling by now how for loops can be quite useful with data organized this way.
For instance, to print all the people's names, you can do something like this:

Neat, eh?
But you can do more, lots more. Let's find out how!

Say you want to find out who the shortest person is. What would you do? You can look back at the example of finding the smallest number, and think about how you can make it fit in this problem.
Thought about it enough? How about something like this:

That would work, no? At least it would tell you what the shortest height was. Can you figure out how to display the name of the person with the shortest height? In our case, it would be "Fred"?

Assignment 4.1: try and figure out how to do that.

One way (out of many, of course), is to do it like this:

But I want to show you another way to do the same, a way that I find to be slightly more elegant:

Although its slightly longer, I find it to be more elegant. Lets see how it works.

First, you probably noticed the new keyword "None". To see what it is, you can do something like this:
>>> type(None)
Basically, Python will tell you that it's a new type of information, like integers and strings. It's a "None" type. A None type can only be None. Not very useful? Well, it can be very useful in making the code clearer, because you can use it as a place-holder in situations in which you don't have information yet, but are planning on getting the info soon. In our example, we start out without having found a person. Only when we go through the list of dictionaries, or records in database-speak, do we start assigning found_person actual person data. Of course, the first if, that compares found_person with None is very important. What would happen without it? Try it and find out! Do you get the cryptic error:

TypeError: 'NoneType' object has no attribute '__getitem__'

Basically, the reason for this is that you tried doing found_person["height"] when in fact found_person was still None! It's equivalent to having have done None["height"], which, of course, makes no sense. Not to humans, and apparently, not to Python either. :) Therefore, our first comparison to None is used to make sure that before we do anything else with found_person, it will actually contain person information. Now, what is this person information that we're talking about? You can get a better idea by printing stuff that will help you figure out what the code actually does, or debug information in tech speak:

Now, when you run the code, you should see a lot of information on the screen. Take a look at the output. Can you see that found_person starts out as None, and then becomes a Dict that contains person information? Can you also see that p gets a different dictionary for each item in the people list?

Now the reason that I find this more elegant is that you end up with a dictionary that contains all the relevant information of the person you want, with which you can do as you please in the end. For instance, if I asked you to print the gender of the shortest person, all you have to do is add something like this to the last line of code:

print found_person["gender"]

Pretty easy, no? Try it!

I hope you get a feel for what you can do with database-structured information. Now let's have some fun with it:

Assignment 4.2: find the youngest person in the database ("Fred")

Assignment 4.3: find the youngest female in the database ("Sally")

Assignment 4.4: print the names of all the males in the database (in separate lines, as you loop through the records)

Assignment 4.5: count the number of records in the database (4). HINT: although you can use len(people) to find out, for the sake of exercise, don't.

Assignment 4.6: count the number of males in the database (2) Now you can't use len even if you wanted to! HAHAHA!

You can also play around with the cars information:

Assignment 4.7: find the year of the car with the least amount of miles (2007)

Assignment 4.8: print the miles of all the cars in the database (in separate lines, as you loop through the records)

Assignment 4.9: print the total number of miles for the cars in the database (240,776)

Assignment 4.10: find the average number of miles for the cars in the database (80,258)

Assignment 4.11: find out how many cars are of the make "toyota" in the database (2).

Now here's a challenge:

Assignment 4.12: find the average number of miles for all the cars with make "toyota" in the database (78,472)

Did you have fun? Great! Not easy, but by now, you're getting pretty good with loops and conditionals, aren't you? :) Fancy!

Let's twist things around a bit now. One of the assignments asked you to print the names of all the males. You may have done something like this:

But what if I asked you to do something more general? For instance, I can ask you to actually create a list that contains all the people that are male. In essence, I would be asking you to create a new database-like structure, one very similar to the one we've been using, but with different data. This can be achieved somewhat like this:

Do you see how we created a new, empty list using the empty bracket command:

new_list = []

Then, as we found matching records in the people database, we added them to our new_list using the append command:

new_list.append(p)

Notice that this is structured like this:

list_name DOT append ( item_to_add )

This is a way to tell python to append a new item, in our case, p, to an existing list, in our case, new_list. We will learn more about these sort of things later, but for now, the key idea here is that we used a loop to create a new database structure! Neat, huh? Now, we can use this concept to break up complicated tasks into smaller parts. For instance, if I asked you to calculate the average height of all the males, you can have one loop used to gather all the males in a new list, and another to calculate the average height of new list, something like this:

At first, we're creating a list of males. After that, we check to see if we found any, and if we have, then we have another for loop that figures out the average height, but in the new list.

Assignment 4.13: use this technique to find the average height of everyone born after 1970. (119)

Assignment 4.14: how about the average height of all the females born after 1970? (175) Use one for loop to create a new db of all the females born after 1970, and another for loop to calculate their average height.

Assignment 4.15: find the average height for everyone taller than "Ido". (169) Solve this by first writing a for loop to find Ido's height, then anotherfor loop to find everyone that is taller than Ido, and finally, yet another for loop that calculates the average height in the newly created db.

Very nice.

Welcome to the 2nd dimension

Ok, so this section is going to be a lot more boring than you're probably imagining... :) All I'm really talking about here is a list of lists. In tech-speak, a list is usually called an array, and a list of lists is called a 2-dimensional array, and a list of lists of lists is called a 3-dimensional array, etc, etc. We went over them in the section on playing with information. I find them to be a lot less useful than database-like structures (lists of dictionaries), but as you will soon see, they can come in handy at times. Lists of lists are considered 2-dimensional because you can use them to represent a flat space, kind of like a checkers board, or a simple city map. In the checkers board example, each item in it may represent a little square on the board, or in the city map, each item may represent some small area of land. You can do with it whatever you like. For the sake of example, let's have some rows of baskets containing apples:

>>> apples = [

[3, 8, 4, 0, 3, 4],

[9, 3, 7, 2, 4, 2],

[4, 0, 2, 4, 2, 1]

]

Although not a must, when dealing with 2 dimensional arrays each sub-list contains the same number of items. In our apples example, each row contains 6 baskets, not more and no less. Thus we can say that the above is a 3 x 6 array, that is, it contains three lists which contain 6 items each. You can copy the above into your code and play with it. Do you remember how to use lists of lists? For instance, to get the number of apples in the 3rd basket of the 1st row, you do this:

>>> print apples[0][2] # first get the first row, 0, and then the 3rd basket, 2

What will these do?

>>> type(apples)

>>> type(apples[1])

>>> type(apples[2][3])

>>> len(apples)

>>> len(apples[1])

Do you get a feel for it now? Great! Now let's have some fun with it! How do you count the total number of apples in each row of baskets? You can do something like this:

Can you see how this works? If you're a bit lost, you can add debug information and find out, perhaps something like this:

This prints a lot of information to the screen. But if you follow it, you can see step-by-step what the program does and how it figures things out. Nice! But to describe what it does in human-speak, the program goes through each row of baskets. Then, for each row, it goes through all the baskets in the row, and sums up the number of apples in each basket.

Assignment 4.16: instead of printing the number of apples in each row, print the total number of apples (62)

BTW, one way to make the above code a little bit clearer (for some), is to change the line:

num_apples = num_apples + count

To be:

num_apples += count

Do you see the "+=" command? Basically, it means: added the value of count to whatever value num_apples already has. Thus:
>>> a = 7
>>> a += 3
>>> print a

What will you get? Good! Some find this more confusing. I personally like it, but it's a matter of personal preferences. You can do other things, such as:
>>> a = 10
>>> a *= 3 # multiply the value in a by 3
>>> a -= 4 # subtract the value in a by 4
>>> a /= 2 # divide the value in a by 2
>>> a += 1 # add 1 to the value in a
>>> print a

What will you get? Can you figure it out in your head? Did you get 14? Good! I'll be using this style of programming from now on, just because I feel like it.

In a twist, can you figure out how how to count the number of baskets that contain some number of apples:

Assignment 4.17: write a program that counts the number of basket containing 2 apples. (4)

Assignment 4.18: can you write the program in a way that in the very start, you assign a number to the variable search_for, kind of like: "search_for = 3", and then have the program count the number of baskets that contain search_for apples.

Now, say you wanted to display the row ID that is associated with each row. For instance, the 1st row has ID 0, the second has ID 1, the 3rd 2, etc. The ID is the number that you need to access the row directly:
>>> row_id = 1
>>> print apples[row_id]

See? Well, how would we do that? Here is one option:

Do you see how this works? Try it!
Another, more elegant way to achieve the same thing is to use the enumerate function in Python:

What enumerate does is that given a list (of anything, in our case, a list of lists), for each item in that list it returns a pair of values, in which the first contains the row id, or the index in tech-speak, of the item in the list, while the second value contains the item itself. This lets you write cleaner code by avoid having to keep track of what index you're on (as we did with row_id), it takes care of that for you. Another aspect of enumerate is that this "pair" of values that it returns, instead of being a typical list of two items, is a 2-item tuple. A tuple is basically just a list that you can't change anything in. Once a tuple is created, items in the list can't be added, removed, or replaced. I personally think that it's a bit redundant, they could have just stuck with lists, but for some reason this is how some things work in Python. This isn't very interesting, but just to get a feel for what I'm talking about, you can try these:
>>> t = (3, 4) # a tuple containing two items
>>> type(t)
>>> print t
>>> print t[0]
>>> print t[1]
>>> len(t)
>>> t = (6, 3, 2) # a tuple containing three items
>>> t = (4,) # a tuple containing one item. NOTE: you need the comma in the end there

Woohoo. BTW, the advantage of using a tuple over a list, in some scenarios, is that because it cannot be modified, it can be used as the key in a dictionary. For instance, you can do something like this:
>>> x = {}
>>> x[(5, 6)] = 'hi'

yet this will give you an error:
>>> x[[5, 6]] = 'hi'

That's probably one of the best things about tuples.

Getting back to our topic, enumerate returns a tuple with two items: the index and the original item in the given list. Such is life. Another way to see how it works, is to add debug information (as usual):

Did you notice (I'm sure you have) that in the first example, I have "for (row_id, row) in enumerate..." where as in the latter i have "for tt in enumerate...", and only later do I have "(row_id, row) = tt"? What's going on here? Here I'm taking advantage of another neat little trick in Python, in which you can assign more than one value at once! For instance, doing something like:
>>> x, y = 1, 4
makes x equal 1, and y equal 4.

Another way of writing the same thing is:
>>> x, y = (1, 4)

or like this:
>>> (x, y) = (1, 4)

You can even do the same thing with lists:
>>> a, b, c = [6, 3, 0]

Of course, you'll get an error if the number of items don't match, such as in:
>>> a, b = (65, 3, 4)

This should help you understand the difference in the two examples above. In the first, I'm assigning (row_id, row) directly to the 2-value tuple that enumerate returns. It's a bit like writing:
>>> (row_id, row) = (2, [3, 4, 5])

Where as in the 2nd example, I'm assigning the tuple that enumerate returns to just one variable, tt, which I later use to extract the two values. Sort of like doing something like this:
>>> tt = (2, [3, 4, 5])
>>> (row_id, row) = tt

One last thing about tuples here... If you want to have a tuple with only 1 value, you can't do it like this: (7). Python will treat it as a mathematical expression, which is basically just the number 7. If you want to make this an actual tuple, then you would need to add a comma after the one and only value, like so: (7,).
>>> (x,) = (7,)
Joy!

WHEW! Let's move on, please...

OK, so we figured out how to get the index of each row, and how to add up the items in each row. Let's see what else we can do!

Can you find out the number of apples in the basket that has the most apples? Try it!

Assignment 4.19: find the number of apples in the basket with the most apples (9)

Did you figure it out? Basically, you need to go through each rows of baskets, and then for each basket within, compare the number of apples with some variable that is the place-holder of the most apples that we saw so far. Something like this:

Do you see how this works? Add debug information if you haven't. Great! :)
Now, and you may have seen this coming, what if instead of wanting to find out the number of apples in the basket, we wanted to find out the indexes of the basket with the most apples? The reason I'm using the plural, indexes, instead of the singular, index, is that in our case, we need two indexes to fully identify the basket: the index of the row, as well as the index of the basket within the row. We can use enumerate to help us with this task, but can you figure out how?

Assignment 4.20: print the indexes of the basket with the most apples (row index = 1, basket index = 0)

Well, there are plenty of ways to achieve this, but here's one way: (but seriously, it's tough, but try to figure this out yourself first)

Do you see what happens here? Add debug info if you don't! Instead of keeping two variables, great_row_id and great_basket_id separately, I can "connect" them by using a list (or a tuple, as a matter of fact) to store the two values together:

Do you see what happens? Did you figure out what this does:

apples[great_index[0]][great_index[1]]

Do you see how it gets the number of apples in the basket, based on the index values in great_index? Add debug info if you don't!

Of course, I could have done the same usinga tuple instead of a list:

Do you see that the only difference is the use of "(" and ")" instead of "[" and "]"... Not a very big deal for not a very big difference. What using a tuple instead of a list gives you is that once that tuple is created, it cannot be modified. Amazing, I know.

But my favorite way to do this here is to actually use a dictionary. Can you figure out how? Here's what I mean:

Do you see how I created the dictionary by using "{" and "}"? The reason that I prefer this solution is that it makes the program a lot easier for someone to understand. Compare the previous use of:

apples[great_index[0]][great_index[1]]

With the current:

apples[great_index["row"]][great_index["basket"]]

While it's easy to lose track of what "0" and "1" stand for, "row" and "basket" is pretty straight forward to understand. In my eyes, it makes the program a lot more elegant. I like it only slightly more than using two separate variables, such as great_row_id and great_basket_id as shown a few examples earlier.

Whew! That was a lot of information.

Previously, you had an assignment that for a given number, count the number of baskets that contain that many apples (Assignments 4.17 and 4.18). Now, can you think of how to modify that program to instead of calculate the number of baskets that contain that many apples, actually return a list that contains all the indexes to those baskets?

We can take advantage of the database structure that we learned in the previous section to have the for loop generate a list containing all the indexes of all the baskets containing some number of apples. Good idea, huh? :) I'm glad you like it.

Can you think of how to do that? Well, the records can be in the form that we just saw:

{ "row": row_id, "basket": basket_id }

Now we need to write the appropriate for loop that appends such records to some new list that we create.

Assignment 4.21: try to figure out how to do this. Look at the code that we have above to generate a list of males in the people list. Can you figure out how to make this work with the for loop within a for loop above? You'll probably want to use enumerate. Go! :)

If you couldn't figure it out, well, no worries, here's what I came up with:

Can you figure it out? Great! :)

Now, it's your turn to work hard:

Assignment 4.22: count the number of baskets that have more than 3 apples (8)

Assignment 4.23: instead of a count, actually return the indexes (in db structure) of the baskets that have more than 3 apples.

Assignment 4.24: calculate the average number of apples in baskets that have more than 3 apples. You can use the previous code to get the list of such baskets, and then write anotherfor loop to calculate the average number of apples in them.

Assignment 4.25: find the smallest number of apples in a basket that has more than 3 apples. You can do this in two ways: one using two for loops (one within another, and a more complicated conditional if statement that keeps track of the smallest number of apples that is more than 3), and another using three for loops, one within another to generate the indexes, and a third to find the minimal count within the new list. Try to solve using both methods. Which one do you prefer? Why?

Super Challenge of the Day:
Imagine an organized pile of blocks. 4 blocks deep, 3 blocks wide, and 5 blocks high. This gives you a total of 4 x 3 x 5 = 60 blocks. Now, each block is actually a container that contains some number of marbles. It may have no marbles at all, or many marbles. I can describe it with a 3-dimensional matrix like this:

>>> blocks = [
[ [5, 3, 8, 3, 0],
[4, 2, 4, 3, 2],

[1, 4, 3, 9, 3] ],
[ [3, 4, 2, 4, 2],
[0, 0, 0, 0, 0],

[1, 1, 1, 1, 1] ],
[ [2, 3, 3, 4, 5],
[5, 2, 1, 2, 1],

[7, 8, 6, 7, 8] ],
[ [7, 5, 3, 2, 6],
[3, 3, 6, 0, 9],

[5, 4, 1, 2, 1] ]
]

Assignment 4.26: count the number of blocks that have more than 3 marbles but less than 8 marbles. HINT: you'll need to use a loop within a loop within a loop! Have fun! (You should get 18 marbles)

Assignment 4.27: instead of a count, can you create a db type list of the relevant indexes, a record should look something like this: { "depth": depth_id, "width": width_id, "height": height_id } HINT: if you run into strange problems here, remember references from the lesson on information, it may help you out. Or not. Either way, good luck!

Cry if you have to, but if you can do the above, you have officially mastered loops and conditionals.

Hurrah for Minesweeper!

In this lesson you've learned about some ways to better organize information: databases and matrices. Has anything you learned changed the way you want to solve the previous problems?

How would you represent a Minesweeper cell now?

How would you represent a Minesweeper grid of cells? Say, a grid that has 5 rows containing 4 cells each (for a total of 20 cells).

Can you write the code that checks to see whether or not the game has been lost? That is, has the player clicked on any cells in the grid that contain a mine?

Can you write the code that checks to see whether or not the game has been won? That is, has the player clicked on all the cells that don't have a mine, but not on any cell that does?

Final Note
Now you're ready to learn how to organize your program. Although less algorithmic than loops and conditionals, organizing well is key to making a complicated program look simple!

We'll start with functions, which are a lot of fun (trust me), then talk about classes (which are loads of fun as well), and finally libraries (which are so-so fun, but incredibly useful). Woohoo!

As always, give me feedback, people! If you got stuck somewhere, I really want to hear about it—if I did my job well, you really should not have. So let me know!! Seriously, people. :)

Riddle me this...

Here you have a list of strings. For each string, the character that occurs most often is the one you want. Put all those characters together to see the message....

For instance, if the string is "abcaba", then 'a' occurs 3 times, 'b' 2 times, and 'c' once. Thus 'a' is the hidden letter.

Hint: To go through all the letters of a string, just as in a list, do: