@extends('template')
@section('title')
Lab 7: Part 1. Nested For Loops
@stop
@section('head')
@stop
@section('content')
# Lab 7: Part 1. Nested For Loops
## More nested loops
Create a new file called `nestedLoops.py` in your lab07 folder.
### Task 1. `scatterStars`
Write a function called `scatterStars` that takes a single integer parameter, `num`
and then uses nested for loops to print `num` columns of `num` stars. Each column
of stars ends with a single dash `-`. `scatterStars` is a None function.
__Examples:__
```py
>>> scatterStars(2)
*
*
-
*
*
-
>>> scatterStars(4)
*
*
*
*
-
*
*
*
*
-
*
*
*
*
-
*
*
*
*
-
```
### Task 2. `scatterHeightStars`
Write a function called `scatterHeightStars` that takes `num` and `height` integers
and uses nested for loops to print `num` columns of `height` stars. Each column
of stars ends with a single dash `-`. `scatterHeightStars` is a None function.
__Examples:__
```py
>>> scatterHeightStars(2,5)
*
*
*
*
*
-
*
*
*
*
*
-
>>>
>>> scatterHeightStars(4,3)
*
*
*
-
*
*
*
-
*
*
*
-
*
*
*
-
```
### Task 3. `scatterSpacedStars`
Write a function called `scatterSpacedStars` that takes `num` and `height` integers
and uses nested for loops to print `num` columns of `height` stars. Each column
of stars ends with a single dash `-`. Each successive star in a given column is spaced
one more space to the right than the star above it. `scatterSpacedStars` is a None function.
Here is a disgram that labels the spacing of the stars:
__Examples:__
```py
>>> scatterSpacedStars(2,5)
*
*
*
*
*
-
*
*
*
*
*
-
```
```py
>>> scatterSpacedStars(4,3)
*
*
*
-
*
*
*
-
*
*
*
-
*
*
*
-
```
### Task 4. Mix and Match
Write a function called `mixAndMatch` that takes two lists of strings and a verb (a string), and returns a list of sentences of the form
`word1 verb word2`
where `word1` is from the first list, `word2` is from the second list, and the verb is provided.
__Examples:__
```py
people = ['Andy','Ada','Sohie', 'Peter']
stuff = ['python','chocolate']
bigIdeas = ['my life', 'my sleep', 'my romance']
>>> mixAndMatch(people, stuff, 'likes')
['Andy likes python', 'Andy likes chocolate', 'Ada likes python',
'Ada likes chocolate', 'Sohie likes python', 'Sohie likes chocolate',
'Peter likes python', 'Peter likes chocolate']
>>> mixAndMatch(stuff, bigIdeas, 'rules')
['python rules my life', 'python rules my sleep', 'python rules my romance',
'chocolate rules my life', 'chocolate rules my sleep', 'chocolate rules my romance']
```
### Task 5. Flatten a nested list
Write a function called `flatten` that takes a nested list (a list of
lists) and **returns** a new list that places each row into a single long
list one after the other. Use nested loops in your `flatten` function.
__Examples:__
```py
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:
```py
flatten([[1,2,[3]],[[4]]])
[1, 2, [3], [4]]
```
### Task 6. distribute
Write a function called `distribute(list1, list2)` that takes two
lists and returns a new list of lists. There should be the same number
of nested lists as there are elements in `list1`. Each nested list
contains a combination of one word from `list1` and each word from
`list2`, in other words, each element of `list1` should be distributed
across `list2`. See below for examples.
```py
>>> distribute(['a','b'],['x','y','z'])
[['ax', 'ay', 'az'], ['bx', 'by', 'bz']]
>>> fruit = ['banana', 'pear','apple','grape','peach','strawberry','watermelon']
>>> distribute(fruit,['NY', 'CA'])
[['bananaNY', 'bananaCA'], ['pearNY', 'pearCA'], ['appleNY', 'appleCA'],
['grapeNY', 'grapeCA'], ['peachNY', 'peachCA'],
['strawberryNY', 'strawberryCA'], ['watermelonNY', 'watermelonCA']]
>>> greetings = ['hola','bonjour','ciao','nihao']
>>> distribute(greetings,['ellen','portia'])
[['holaellen', 'holaportia'], ['bonjourellen', 'bonjourportia'],
['ciaoellen', 'ciaoportia'], ['nihaoellen', 'nihaoportia']]
```
### Task 7. Dealing with 2D data
{{--
This part of the lab has a video walkthrough. If you get stuck or want extra
context, you can watch the video below, or click the download link to download
it and watch it later. The full video description on YouTube includes a
breakdown of all of the topics in the video with links to each one.
[Download Video](https://sakai.wellesley.edu/access/content/group/77109fc3-0650-48b0-aeb4-22c7e2e94cba/Lab%209%3A%20Nested%20Loops%20%2B%20Sorting/lab9_2D_data.mp4) | [Download Captions](https://sakai.wellesley.edu/access/content/group/77109fc3-0650-48b0-aeb4-22c7e2e94cba/Lab%209%3A%20Nested%20Loops%20%2B%20Sorting/lab9_2D_data.sbv)
--}}
In plant ecology, a technique called [quadrant
sampling](https://www.bbc.co.uk/bitesize/guides/zmyj6sg/revision/9) 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:
```py
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:
```py
4 0 0 1
1 3 0 0
0 5 0 0
1 0 0 1
```
{{-- commenting out to streamline this lab --}}
{{--
If we instead decided that each inner list was a **column**, the list
above would instead represent this 2D matrix:
```py
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).
--}}
Each inner list, then, is a **row**, so we will **first index by row** (i.e., vertical position)
and **second by column** (i.e., horizontal position). For example,
`flowerCounts[2][1]` stores the value 5 (3rd row, 2nd column).
#### Task 7A. 2-dimensional sum
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:__
```py
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]]
```
```py
>>> gridSum(test)
45
>>> gridSum(mini)
56
>>> gridSum(flowerCounts)
16
```
#### Task 7B. Full Coverage
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:__
```py
>>> gridNoZeroes(test)
True
>>> gridNoZeroes(mini)
True
>>>gridNoZeroes(flowerCounts)
False
```
#### Task 7C. Max Cell
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:__
```py
>>> maxCell(test)
(2, 2)
>>> maxCell(mini)
(1, 0) # note this is 2nd row, 1st column
>>> maxCell(flowerCounts)
(2, 1) # 3rd row 2nd column
```
{{-- commenting out with these shortened 75 minute friday labs Term 1
--}}
{{--
## OPTIONAL Extra Tasks
### 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:__
```py
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.
```py
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:__
```py
>>> 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:__
```py
>>> 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
```
--}}
@include('/labs/lab07/_toc')
@stop