Lab 7: Part 1. Nested For Loops

More nested loops

Create a new file called `nestedLoops.py` in your lab07 folder.

Write a function called `printCalendarDays` that takes a list of numbers indicating the number of days in each month, and prints a vertical calendar, with each month labeled and one day number per line. Use nested loops in your `printCalendarDays` function.

Examples:

``````printCalendarDays([5, 5, 6])

Month is 1
1
2
3
4
5

Month is 2
1
2
3
4
5

Month is 3
1
2
3
4
5
6``````

The input for the Gregorian calendar on a non-leap year would be:

``[ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]``

although the output is too long to be useful as a test. As another example, the 2020 Year of the Rat in the Chinese Lunar Calendar would be:

``[ 29, 30, 30, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30 ]``

(The 1st day of the 1st month of the 2020 Chinese Lunar year falls on the 25th of January 2020 in the Gregorian calendar.)

Write a function called `triangle` that takes one integer and prints a triangle the given height. It is possible to use string multiplication to achieve this, but if you do, consider how you could use nested loops in your `triangle` function instead. Think about the relationship between the number of stars in a row and the position of that row in the triangle. See comments in `triangle(4)` example below.

Note: In Python 3, you can change the `end` in print so that subsequent prints continue on the same line. (the default `end` with print is a newline).

``````for num in range(3):
print(5, end=" ")
print ()
print ('hello')``````

will print

``````5 5 5
hello``````

Likewise:

``````for letter in ["A", "B", "C"]:
print(letter, end="-")
print("done")
print("after")``````

will print

``````A-B-C-done
after``````

Examples:

``````triangle(2)
*
* *

triangle(4)
*         # 1st row, 1 star
* *       # 2nd row, 2 stars
* * *     # 3rd row, 3 stars
* * * *   # 4th row, 4 stars

triangle(7)
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *``````

Write a new function called `numTriangle` based on your `triangle` function. `numTriangle` creates triangles are made of numbers instead of asterisks, and it is no longer possible to use string multiplication to avoid a nested loop here:

``````numTriangle(4)

1
1 2
1 2 3
l 2 3 4

numTriangle(7)

1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6 7``````

Now, make a new function called `invertedTriangle` that prints triangles starting with the longest side and whittling down to the short side.

``````invertedTriangle(3)
1 2 3
1 2
1

invertedTriangle(1)
1

invertedTriangle(7)
1 2 3 4 5 6 7
1 2 3 4 5 6
1 2 3 4 5
1 2 3 4
1 2 3
1 2
1``````

Task 3. Dealing with 2D data

In plant ecology, a technique called quadrant sampling can be used to estimate the number of plants in a given area by counting individual plants within the cells of a square grid:

The data from such a sample could be represented as a nested list (a list of lists). Which might look like this:

``flowerCounts = [[4, 0, 0, 1], [1, 3, 0, 0], [0, 5, 0, 0], [1, 0, 0, 1]]``

If we imagine that each inner list is a row, the list above can be seen to represent the following 2D matrix:

``````4 0 0 1
1 3 0 0
0 5 0 0
1 0 0 1``````

If we instead decided that each inner list was a column, the list above would instead represent this 2D matrix:

``````4 1 0 1
0 3 5 0
0 0 0 0
1 0 0 1``````

Although we have to make a clear decision about whether the inner lists are rows or columns, we can access data from a particular row and column by indexing twice: once to get the row (or column) and again to get the individual entry within that row (or column).

To avoid confusion, let's assume from here on out that each inner list will be a row, so we will first index by row (i.e., vertical position) and second by column (i.e., horizontal position).

What if we just want to know the total number of plants counted anywhere in the grid? Write a function called `gridSum` that accepts a list of lists as an argument, and returns the sum of all numbers from each row of the grid.

Examples:

``````test = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
mini = [[8, 4], [23, 21]]
flowerCounts = [[4, 0, 0, 1], [1, 3, 0, 0], [0, 5, 0, 0], [1, 0, 0, 1]]``````
``````>>> gridSum(test)
45
>>> gridSum(mini)
56
>>> gridSum(flowerCounts)
16``````

What if we just wanted to know whether we found at least one specimen in each grid cell? Define a function called `gridNoZeroes` that returns `True` if a grid has no zeroes in it, and `False` if it has at least one cell that contains a zero.

Examples:

``````>>> gridNoZeroes(test)
True
>>> gridNoZeroes(mini)
True
>>>gridNoZeroes(flowerCounts)
False``````

Which grid cell had the highest number? Let's write a function that will return a (row, column) pair indicating the location of the grid cell with the highest number (counting from 0). For example, in our `test` grid, the answer would be `(2, 2)` because the largest number is 9 in row 2 and column 2 (counting from 0 instead of 1).

Write a function called `maxCell` that takes a grid and returns the location of the largest element.

Examples:

``````>>> maxCell(test)
(2, 2)
>>> maxCell(mini)
(1, 0) # note this is 2nd row, 1st column
>>> maxCell(flowerCounts)
(2, 1) # 3rd row 2nd column``````

Task 4. Flatten a nested list

Write a function called `flatten` that takes a nested list (a list of lists) and returns a new list that has the contents of the flattened original list. Use nested loops in your `flatten` function.

Examples:

``````flatten([[0,1],[2,3,4]])
[0, 1, 2, 3, 4]

flatten([[2,4,6,8],[],[10,12]])
[2, 4, 6, 8, 10, 12]

flatten([[1,2,3],[4,5,6],[7,8]])
[1, 2, 3, 4, 5, 6, 7, 8]

flatten([[1,2,3],[4,5,6],[7,8],[9],[10]])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]``````

Note that `flatten` only flattens lists that are nested one level deep. Deeper nested lists are not flattened, as can be seen in the example below:

``````flatten([[1,2,[3]],[[4]]])
[1, 2, [3], [4]]``````

Extra 1. Multiplication Charts

In the lecture notebook, you saw a function for creating multiplication tables, but it did not bother with alignment, so they were hard to read.

Write a function called `multTableSpaced` that prints a multiplication table, with the provided number of rows and columns, aligned so that it is easier to read. To align the columns, you just need to make sure that whenever you print a number, you print extra spaces so that shorter numbers take up up just as much space as longer numbers. It might help to write a function `printSpacedNumber(num)` that just takes care of that task, e.g., `printSpacedNumber(7)` would print `␣7`, (where `␣` represents the extra space) while `printSpacedNumber(12)` would just print `12`.

Examples:

``````multTableSpaced(5,6)
1   2   3   4   5   6
2   4   6   8  10  12
3   6   9  12  15  18
4   8  12  16  20  24
5  10  15  20  25  30

multTableSpaced(5,10)
1   2   3   4   5   6   7   8   9  10
2   4   6   8  10  12  14  16  18  20
3   6   9  12  15  18  21  24  27  30
4   8  12  16  20  24  28  32  36  40
5  10  15  20  25  30  35  40  45  50

multTableSpaced(8,9)
1   2   3   4   5   6   7   8   9
2   4   6   8  10  12  14  16  18
3   6   9  12  15  18  21  24  27
4   8  12  16  20  24  28  32  36
5  10  15  20  25  30  35  40  45
6  12  18  24  30  36  42  48  54
7  14  21  28  35  42  49  56  63
8  16  24  32  40  48  56  64  72``````

Note: ideally, your `multTableSpaced` will work for longer numbers, as shown below. To pull this off, you will first need to figure out the width of the largest number (which will always be the result of multiplying the two largest numbers in the table). Your `printSpacedNumber` function will need to take two arguments: the number to print, and the width to space it to.

``````multTableSpaced(12,12)
1   2   3   4   5   6   7   8   9  10  11  12
2   4   6   8  10  12  14  16  18  20  22  24
3   6   9  12  15  18  21  24  27  30  33  36
4   8  12  16  20  24  28  32  36  40  44  48
5  10  15  20  25  30  35  40  45  50  55  60
6  12  18  24  30  36  42  48  54  60  66  72
7  14  21  28  35  42  49  56  63  70  77  84
8  16  24  32  40  48  56  64  72  80  88  96
9  18  27  36  45  54  63  72  81  90  99 108
10  20  30  40  50  60  70  80  90 100 110 120
11  22  33  44  55  66  77  88  99 110 121 132
12  24  36  48  60  72  84  96 108 120 132 144``````

Extra 2A. printCalendarWeeks

Now that we've got a nice-looking multiplication table, can we do better with the formatting of our calendar (from Task 1 above)?

Write a function called `printCalendarWeeks` that takes a list of month lengths to print and prints a calendar where weeks are grouped in rows. Use your `printSpacedNumber` function from Extra 1 above to align the numbers (you can assume that the longest date in each month always has two digits).

Example:

``````>>> nl.printCalendarWeeks([12, 15, 23])

Month is 1
1  2  3  4  5  6  7
8  9 10 11 12

Month is 2
1  2  3  4  5  6  7
8  9 10 11 12 13 14
15

Month is 3
1  2  3  4  5  6  7
8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23``````

Extra 2B. `printCalendarFancy`

In a real calendar, the day numbers are aligned in columns according to the day of the week, so that the 1st day of a month isn't always in the 1st column.

Write a function called `printCalendarFancy` that takes a list of days-per-month, and a starting column for the 1st day of the 1st month, and prints a calendar where weeks are grouped in rows. If a week splits between months, the spacing is maintained.

Examples:

``````>>> printCalendarFancy([12, 15, 23], 4)

Month is 1
1  2  3
4  5  6  7  8  9 10
11 12

Month is 2
1  2  3  4  5
6  7  8  9 10 11 12
13 14 15

Month is 3
1  2  3  4
5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23

>>> printCalendarFancy([31, 29, 31, 30], 3) # first 4 months of 2020

Month is 1
1  2  3  4
5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Month is 2
1
2  3  4  5  6  7  8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29

Month is 3

1  2  3  4  5  6  7
8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

Month is 4
1  2  3  4
5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30``````