Today we'll talk about **sequences**, an abstract type that exemplified by two concrete types: `str`

and `list`

.

We will talk about the common properties of sequences, and how they are using in enabling another way for **looping** execution.

**Table of Contents**

Sequences in Python are ordered collections of values. We have already seen one example of a sequence: a string. A string is an ordered collection of characters. There are others as well.

**Sequences in Python**:

- Strings: an ordered collection of characters
- Lists: an ordered collection of any type
- Ranges: an ordered collection of integers

Many times we use sequences in conjunctions with loops. Sequences hold data and loops provide a way to look through the sequence one element at a time. We've already seen an example of that from last lecture with `countVowels`

.

In the last lecture we saw that we could use a while loop to count the number of vowels in a string. How did we do that? We iterated over each letter by getting the index of each letter. The index allowed us to extract each element (i.e., letter) from the string sequence. We can use this approach with **any** sequence as we will see shortly.

In [1]:

```
def isVowel(letter):
"""a predicate that returns true for letters that are vowels.
"""
return letter.lower() in "aeiou"
```

In [2]:

```
def countVowels(word):
"""an accumulation function that returns the number of vowels in a word.
"""
vowelCount = 0
i = 0
while i < len(word):
if isVowel(word[i]):
vowelCount += 1
i += 1
return vowelCount
```

In [3]:

```
countVowels("perspicacious")
```

Out[3]:

Here is another example using a while loop to print out all the letters in a string. You can see we use the same paradigm by going through each index of the string to extract the letter and print it.

In [4]:

```
word = "Boston"
i = 0
while i < len(word):
print(word[i])
i += 1
```

While (pun intended) the above code works perfectly fine, there is actually a syntatically cleaner way to write the code above. If we use a **for** loop, we do not need to worry about using the indices to extract each element from the string.

`for`

loops with strings¶A `for`

loop is also a structure to repeat code. Anything that you want to do with a `for`

loop can be done with a `while`

loop, but a `for`

loop is generally syntatically cleaner and more concise. In Computer Science, we say that a `for`

loop is **syntactic sugar**. Syntactic sugar are syntactical elements of a programming language that are redundant (i.e., can be done by some other paradigm) but is designed to make things easier to read or express. We can always use a `while`

loop to handle our iteration needs but a `fo`

r loop offers a more readable and concise option for certain problems.

**Important**: Any `for`

loop can be written as a `while`

loop, but not all `while`

loops can be written as for loops. We will see why shortly.

The cell below shows the `fo`

r loop version of the code above to print each letter from a string.

All for loops take two things:

- An iteration variable - here this is
`letter`

. An iteration variable is a new variable that is declared by the for loop. Each element of the sequence is set to`letter`

. - A sequence - here this is the string held in
`word`

.

The code that we repeat is the print statement. We repeat for as many elements as there are in the sequence. Notice how we do **not** need indices.

In [5]:

```
word = 'Boston'
for letter in word:
print('letter has the value', letter)
```

`countVowels`

¶Below is the for loop approach to `countVowels`

.

In [6]:

```
def countVowels(word):
"""an accumulation function that returns the number of vowels in a word.
"""
vowelCount = 0 # accumulation variable
for letter in word:
if isVowel(letter):
vowelCount += 1
return vowelCount
```

In [7]:

```
countVowels("perspicacious")
```

Out[7]:

`countLetter`

¶Using a `for`

loop, write a function that returns the number of times a certain character `char`

appears in a string `word`

.

In [8]:

```
def countLetter(word, char):
# Your code here
charCount = 0
for letter in word:
if letter == char:
charCount += 1
return charCount
```

In [9]:

```
countLetter("amanuensis", "s")
```

Out[9]:

In [10]:

```
countLetter("amanuensis", "a")
```

Out[10]:

For loops iterate over sequences. Strings are an example of a sequence. The for loop assigns each character of the string to the iteration variable. This section discusses a new type called Lists which are also sequences.

Lists are another type in Python just like strings or ints. Lists are an ordered sequence of values. Lists are declared with square brackets and items are separated by commas. Lists are an example of a data structure. Lists are meant to store data where the order of the data matters.

In [11]:

```
[42, 3, -7]
```

Out[11]:

Lists can have the same type or different types of values.

In [12]:

```
[True, 3.4, "hello"]
```

Out[12]:

Similar to other types that we have seen: *integer*, *float* and *string*, there is a built-in function with the name of the type that generates values of that types by converting from a type to another. The function `list`

below converts strings to a list of string characters.

In [13]:

```
list("Wendy Wellesley")
```

Out[13]:

**Question:** Will it work the same way with integers?

In [14]:

```
list(100)
```

**Question:** What if you convert the number into a string first, and then try to turn it into a list. Will that work?

In [15]:

```
list(str(3.14159265))
```

Out[15]:

We can use the same syntax for iterating over lists just as we did with strings. With lists, the iteration variable gets assigned to each element in the list.

In [18]:

```
books = ["Beloved", "All the light we cannot see", "The Song of Achilles", "Othello"]
```

In [19]:

```
for book in books:
print("Book:", book)
```

`printVowelBooks`

¶Below write a function called `printVowelBooks`

that **prints out** all the books in a list of books that start with a vowel. `printVowelBooks`

should take a single parameter that is a list of book.

In [20]:

```
# Your code here
def printVowelBooks(books):
for book in books:
if isVowel(book[0]):
print(book)
```

In [21]:

```
printVowelBooks(books) # should print out two books from the list above
```

Strings and lists are sequences. Another type of sequence is a range. A range produces a sequence of ordered numbers. To see why a range might be useful, consider the example below:

In [22]:

```
#Let's check how many divisors a number has.
number = 8
divisors = 0
for candidate in [1,2,3,4,5,6,7,8]:
if number % candidate == 0:
divisors = divisors + 1
print(number, 'has', divisors, 'divisors')
```

In [23]:

```
# And again...
number = 11
divisors = 0
for candidate in [1,2,3,4,5,6,7,8,9,10,11]:
if number % candidate == 0:
divisors = divisors + 1
print(number, 'has', divisors, 'divisors')
```

Notice that we have to hardcode the list of numbers. This means we can't use this code on any number. We'll learn about a new tool that lets us generalize our divisor counting now.

`range`

¶The first argument of `range`

starts the sequence, and all numbers up to (but not including) the second argument are generated.

In [24]:

```
range(0, 10)
```

Out[24]:

In [25]:

```
type(range(0, 10))
```

Out[25]:

The `range`

function returns an object that holds the sequence. To see the contents of range's sequence, pass it into the built-in `list`

function that will return a list of numbers. We will talk more about the `list`

function in subsequent classes.

In [26]:

```
list(range(0, 10))
```

Out[26]:

We'll get an empty list when the two arguments are the same, or the first is greater than the second:

In [27]:

```
a = range(3, 3)
b = range(3, 1)
print(list(a))
print(list(b))
```

Omiting the first argument means we want to start at 0 by default

In [28]:

```
list(range(8))
```

Out[28]:

`range`

with three arguments¶`range`

takes a third argument, the value of the step between two generated values. If this value is missing, by default step is 1.

In [29]:

```
# The even numbers
list(range(0, 10, 2))
```

Out[29]:

In [30]:

```
# Stepping by 10
list(range(3, 70, 10))
```

Out[30]:

**The step can be negative.** When the step is negative, the start needs to be greater than the end of the range.

In [31]:

```
list(range(10, 0, -1))
```

Out[31]:

**Your turn:** Write the range to produce all **nonnegative** integers up to and including 50 that are divisible by 5:

In [32]:

```
# Your code here:
nums = range(0,51,5)
list(nums)
```

Out[32]:

We can iterate over this sequence just like any other sequence!

In [33]:

```
for i in range(11): # Starts at 0, goes up to n-1
print(i)
```

If we want the range of numbers from 1 up to and including `number`

, here's one way we can do that:

In [34]:

```
number = 11
for i in range(number):
print(i + 1) # Add 1 to the number before using it
```

Remember that we can also explicitly set both the start and end values for `range`

:

In [35]:

```
number = 13
for i in range(1, number + 1): # Ask range to give us the right numbers directly
print(i)
```

Now we can generalize our divisor counting!

In [36]:

```
def countDivisors(number):
"""A function that returns the total of divisors for a given number.
"""
divisors = 0
for candidate in range(1, number + 1):
if number % candidate == 0:
divisors = divisors + 1
return divisors
```

In [37]:

```
countDivisors(8)
```

Out[37]:

In [38]:

```
countDivisors(11)
```

Out[38]:

`doublesUpTo`

¶Write a function `doublesUpTo`

which takes a number `x`

and **prints out** each of the integers up to and including `x`

doubled. For example, `doublesUpTo(4)`

should print out

```
0
2
4
6
8
```

In [39]:

```
# Your code here
def doublesUpTo(x):
for i in range(x+1):
print(i * 2)
doublesUpTo(4)
```

You can also use `range`

to repeat some code multiple times even if you don't use the iteration variable.

In [40]:

```
for _ in range(10): # The variable name _ is just a normal variable name,
print('Say this tongue twister ten times fast!') # but some programmers use it as a convention to indicate to
# a reader of the code that syntax requires a variable name
# but they don't plan to use the value stored in that variable.
```

To prove that the underscore or using the underscore in a variable name is valid, consider the syntax below:

In [41]:

```
_ = 3
```

In [42]:

```
_
```

Out[42]:

Write a function called `sumIntegers`

that takes a number `numOfInts`

specifying the number of integers to sum and prompts the user `numOfInts`

times to provide an integer. The function should return the sum of all those integers. Here is a sample call below.

```
In [1]: sumIntegers(3)
Provide an integer: 4
Provide an integer: 2
Provide an integer: 1
Out [1]: 7
```

In [43]:

```
# Your code here
def sumIntegers(numOfIntegers):
count = 0
for _ in range(numOfIntegers):
count += int(input("Provide an integer: "))
return count
```

In [45]:

```
sumIntegers(3)
```

Out[45]:

In [46]:

```
sumIntegers(5)
```

Out[46]:

Up to this point, we have seen how for loops iterate over a sequence by setting the iteration variable to each value in the sequence. For a string, the iteration variable is set to each letter in the string. For a list, the iteration variable is set to each element in the list. For a range, the iteration variable is set to each number in the range. All of the for loops we have examined so far are meant to extract each value from the sequence. When we iterate directly over a sequence like this, we call it a "value loop".

In [47]:

```
sequence = "computer"
for value in sequence:
print(value)
```

In [48]:

```
sequence = [True, None, 3]
for value in sequence:
print(value)
```

In [49]:

```
sequence = range(4)
for value in sequence:
print(value)
```

While the for loop patterns we have seen so far help us solve many problems, they do not encompass all the ways we can use loops. For example, how would I write a loop that prints out every other element in a list? We can, of course, use a while loops as we have seen before, but how could we do it with a **for loop**?

In [50]:

```
# How can I print out all the letters from this list
seq = ['a', 1, 'b', 2, 'c', 3]
for element in seq:
print(element) # this prints out every element...
```

The position of elements is important in solving this problem. The loop above only gives us the values in the sequence. We need more information! We need the positions of the values!

How can I get the position of the elements? Here we can use `range`

and `len`

to help us. The solution to this problem is to generate a new sequence whose elements are the indices of the original sequence. For example, given the list `['a', 1, 'b', 2, 'c', 3]`

, we want to be able to generate a sequence that has the numbers 0, 1, 2, 3, 4, and 5.

In [51]:

```
indices = range(len(seq)) # a sequence of indices
list(indices) # list is only being used here to show you the elements in range
```

Out[51]:

Now that we have a sequence of the indices, we will loop over the indices and set the iteration variable to each index in the sequence of indices. We call this an **index loop** because we set the iteration variable to each index in the original sequence as opposed to its value.

In [52]:

```
# this code prints out all the indices from the original sequence
for index in range(len(seq)):
print(index)
```

The great thing about indices is that we can also use the index to get value at that index as well using the subscripting operator (i.e., `[]`

).

In [53]:

```
for index in range(len(seq)):
print("Index:", index, "Value:", seq[index])
```

Now that we know how to generate the indices from any sequence, we can solve our original problem. Note here that we use the iteration variable `i`

. This is a common choice for index loops as `i`

indicates an index. We can, of course, choose something else but this is a common convention.

In [54]:

```
# a loop that prints only the letters from the list ['a', 1, 'b', 2, 'c', 3]
for i in range(len(seq)):
if i % 2 == 0: # we use the index to help us filter out the positions we care about
print(seq[i])
```

To summarize, let's make index loops for each type of sequence that we know.

In [55]:

```
sequence = "computer"
for i in range(len(sequence)):
print("Index:", i, "Value:", sequence[i])
```

In [56]:

```
sequence = [4.1, 3, 'a']
for i in range(len(sequence)):
print("Index:", i, "Value:", sequence[i])
```

In [57]:

```
sequence = range(100, 108, 2)
for i in range(len(sequence)):
print("Index:", i, "Value:", sequence[i])
```

Sometimes your loop needs both the values in a sequence as well as their indices. For example, let's imagine that you're running an amusement park. When people want to get on a ride, they put their initials on a waitlist. Then, you display the waitlist on a screen so folks can know how close to the front of the line they are. So for example, if some prominent American figures wanted to ride, the screen might show something like:

```
#0: BHO
#1: MLRO
#2: MAO
#3: SO
#4: AOC
#5: NPP
#6: RBG
```

Our first attempt might look like this:

In [58]:

```
waitlist = ['BHO', 'MLRO', 'MAO', 'SO', 'AOC', 'NPP', 'RBG']
for name in waitlist:
print(name)
```

But this doesn't print the indices, which are an important piece of information that we wanted to display. In this case, we will often use `range`

to generate indices, like so:

In [59]:

```
waitlist = ['BHO', 'MLRO', 'MAO', 'SO', 'AOC', 'NPP', 'RBG']
for spot in range(len(waitlist)): # Range generates the indices
name = waitlist[spot] # Use indexing to get the value
print('#' + str(spot) + ': ' + name) # Perform a calculation using both value and index.
```

In [60]:

```
# Once Barack gets off the list and is on the rollercoaster, you can see that everyone else has moved up
waitlist = ['MLRO', 'MAO', 'SO', 'AOC', 'NPP', 'RBG']
for spot in range(len(waitlist)):
name = waitlist[spot]
print('#' + str(spot) + ': ' + name)
```

Use `range`

to make `x`

hold the same list as goal in each cell. The first one has been done for you as an example:

In [61]:

```
goal = [0,1,2,3,4]
# Your code here:
x = list(range(5))
```

In [62]:

```
assert goal == x
print('Success!')
```

In [63]:

```
goal = [0,1,2,3,4,5,6,7]
# Your code here:
x = list(range(8))
```

In [64]:

```
assert goal == x
print('Success!')
```

In [65]:

```
goal = [1,2,3,4]
# Your code here:
x = list(range(1, 5))
```

In [66]:

```
assert goal == x
print('Success!')
```

In [67]:

```
goal = [0,2,4,6]
# Your code here:
x = list(range(0, 8, 2))
```

In [68]:

```
assert goal == x
print('Success!')
```

In [69]:

```
goal = [5, 8, 11, 14]
# Your code here:
x = list(range(5, 15, 3))
```

In [70]:

```
assert goal == x
print('Success!')
```

In [71]:

```
goal = [100, 200]
# Your code here:
x = list(range(100, 300, 100))
```

In [72]:

```
assert goal == x
print('Success!')
```

Write a function that uses a `for`

loop to print out all the letters in the string `s`

, one per line

In [73]:

```
s = 'abcdef'
# Expected output:
'''
a
b
c
d
e
f
'''
# Your code here:
for let in s:
print(let)
```

Write a function that uses a `for`

loop to print out all the numbers in the list `nums`

In [74]:

```
nums = [1, 6, 2, 9, 10]
# Expected output:
'''
1
6
2
9
10
'''
# Your code here:
for num in nums:
print(num)
```

Write a function that uses a `for`

loop to print out each of the numbers in the list `nums`

divided by 2

In [75]:

```
nums = [1, 6, 2, 9, 10]
# Expected output:
'''
0.5
3.0
1.0
4.5
5.0
'''
# Your code here
for num in nums:
print(num/2)
```

Write a function `countEvens`

that returns the number of even numbers in a list

In [76]:

```
# Your code here
def countEvens(nums):
evens = 0
for n in nums:
if n % 2 == 0:
evens += 1
return evens
```

In [77]:

```
assert countEvens([1,2,3,4,5,6]) == 3
assert countEvens([1,1,1,1,1,1]) == 0
assert countEvens([2]) == 1
print('Correct!')
```

Write a function `countOdds`

that returns the number of odd numbers in a list

In [78]:

```
# Your code here
def countOdds(nums):
odds = 0
for n in nums:
if n % 2 == 1:
odds += 1
return odds
```

In [79]:

```
assert countOdds([1,2,3,4,5,6]) == 3
assert countOdds([1,1,1,1,1,1]) == 6
assert countOdds([2]) == 0
print('Correct!')
```

Write a function `countDoubleDigits`

that returns the number of double digit numbers in a list

In [80]:

```
# Your code here
def countDoubleDigits(nums):
dds = 0
for n in nums:
if n > 9 and n < 100:
dds += 1
return dds
```

In [81]:

```
assert countDoubleDigits([1,2,3,4,5,6]) == 0
assert countDoubleDigits([10,112,112,12,71,1]) == 3
assert countDoubleDigits([15]) == 1
print('Correct!')
```

Write a function `countNegatives`

that returns the number of negative numbers in a list

In [82]:

```
# Your code here
def countNegatives(nums):
negs = 0
for n in nums:
if n < 0:
negs += 1
return negs
```

In [83]:

```
assert countNegatives([1,2,-3,-4,-5,-6]) == 4
assert countNegatives([1,1,1,1,1,1]) == 0
assert countNegatives([-2]) == 1
print('Correct!')
```

Write a function `tongueTwister`

that takes a string and prints the string 5 times

In [84]:

```
# Your code here
def tongueTwister(phrase):
for _ in range(5):
print(phrase)
```

In [85]:

```
#Expected output:
'''
She sells sea-shells by the sea shore
She sells sea-shells by the sea shore
She sells sea-shells by the sea shore
She sells sea-shells by the sea shore
She sells sea-shells by the sea shore
'''
tongueTwister('She sells sea-shells by the sea shore')
```

Write a for loop that iterates over the given string, printing out each index and letter in the string

In [86]:

```
s = 'koala'
#Expected output
'''
0 k
1 o
2 a
3 l
4 a
'''
# Your code here
for index in range(len(s)):
print(index, s[index])
```

Write a for loop that iterates over the given list, printing out the product of each element and its index.

In [87]:

```
nums = [4, 5, 9]
# Expected output
'''
0 4
1 5
2 9
'''
# Your code here
for i in range(len(nums)):
val = nums[i]
print(i, val)
```

Write a for loop that iterates over the given list, printing out the product of each value with its index

In [88]:

```
nums = [4, 5, 9]
# Expected output
'''
0
5
18
'''
# Your code here
for i in range(len(nums)):
val = nums[i]
print(i * val)
```

Write a for loop that iterates over the given list, printing out each word. If the word's index in the list is greater than 2, add an exclamation mark to the end of the word

In [89]:

```
nums = ['I', 'welcome', 'you', 'to', 'my', 'delightful', 'garden']
# Expected output
'''
I
welcome
you
to!
my!
delightful!
garden!
'''
# Your code here
for i in range(len(nums)):
val = nums[i]
if i > 2:
print(val + '!')
else:
print(val)
```

A sequence, is a series of items for which the relative order to one-another matters. In Python, a sequence is the parent class for strings, lists, and ranges. This way, all of these classes will share their behavior (in terms of what operations can be applied to them), but they also will have differences, which we will discuss in the coming lectures.

In [90]:

```
word = "Boston"
digits = [1, 2, 3, 4]
digRange = range(1, 5)
```

In [91]:

```
word
```

Out[91]:

In [92]:

```
digits
```

Out[92]:

In [93]:

```
digRange
```

Out[93]:

**Indexing:** We access an element by using the `[ ]`

operator and a number that is the index of the element in the sequence.

In [94]:

```
word[2]
```

Out[94]:

In [95]:

```
digits[2]
```

Out[95]:

In [96]:

```
digRange[2]
```

Out[96]:

**Finding length:** Because sequences consist of zero or more items, we can use `len`

to find how many items they contain.

In [97]:

```
len(word)
```

Out[97]:

In [98]:

```
len(digits)
```

Out[98]:

In [99]:

```
len(digRange)
```

Out[99]:

**Concatenation:** Sequences can be concatenated by using the operators '+' or '*'

In [100]:

```
word + " Globe"
```

Out[100]:

In [101]:

```
digits + [4]
```

Out[101]:

Note that concatenation is not supported for range objects.

In [102]:

```
digRange + range(4)
```

In [103]:

```
word * 3
```

Out[103]:

In [104]:

```
digits * 2
```

Out[104]:

In [105]:

```
digRange * 2
```

**The membership operator in:** this operator returns True when an item is part of the sequence, and False otherwise

In [106]:

```
't' in word
```

Out[106]:

In [107]:

```
'a' in word
```

Out[107]:

In [108]:

```
'ton' in word
```

Out[108]:

In [109]:

```
'bos' in word
```

Out[109]:

In [110]:

```
1 in digits
```

Out[110]:

In [111]:

```
5 in digits
```

Out[111]:

In [112]:

```
1 in digRange
```

Out[112]:

In [113]:

```
5 in digRange
```

Out[113]:

In [114]:

```
def isVowel2(char):
"""Use `in` for a simpler definition of isVowel"""
return char.lower() in 'aeiou'
```

In [115]:

```
def testIsVowel2(char):
"""Helper function to test isVowel"""
print("isVowel2('" + char + "')", "=>", isVowel2(char))
```

In [116]:

```
testIsVowel2('d')
testIsVowel2('D')
testIsVowel2('a')
testIsVowel2('E')
testIsVowel2('i')
testIsVowel2('O')
testIsVowel2('u')
testIsVowel2('y')
```

**Slicing:** this operation uses two indices (a start and a stop) to create a subsequence of items between the two indices.

In [117]:

```
word[1:4]
```

Out[117]:

In [118]:

```
digits[1:4]
```

Out[118]:

In [119]:

```
digRange[1:4]
```

Out[119]:

If the first index is omitted, the start index is by default 0.

In [120]:

```
word[:3]
```

Out[120]:

In [121]:

```
digits[:3]
```

Out[121]:

In [122]:

```
digRange[:3]
```

Out[122]:

If the stop index is greater than the length of the sequence, Python doesn't return an error, it returns the sequence until the end.

In [123]:

```
word[3:10]
```

Out[123]:

But, we can omit the stop index entirely and get the rest of the subsequence:

In [124]:

```
word[3:]
```

Out[124]:

We can use a third parameter, step, with the slicing operator. Step works just like step for range, skipping a certain number of items.

In [125]:

```
word[0:6:2]
```

Out[125]:

In [126]:

```
digits[0:5:2]
```

Out[126]:

In [127]:

```
digRange[0:5:2]
```

Out[127]:

We can omit the stop argument as before, and Python automatically will look until the end of the sequence.

In [128]:

```
digits[0::2]
```

Out[128]:

**Reversing through slicing:** because Python allows negative indexing (see slide 15), by using step -1, we can reverse a sequence.

In [129]:

```
word[::-1]
```

Out[129]:

In [130]:

```
digits[::-1]
```

Out[130]:

In [131]:

```
digRange[::-1]
```

Out[131]:

**From strings to lists**

We can create a list from a string using the `list`

function.

In [132]:

```
list(word)
```

Out[132]:

We can also produce a list by splitting a string at spaces. This is commonly use to split a sentence into a list of words.

In [133]:

```
phrase = "New England's weather is unpredictable."
phrase.split()
```

Out[133]:

When the `split`

method doesn't take arguments, it splits by default at the white space. If needed, you can split at some other character.

In [134]:

```
message = "I woke up. The sun rose too. The sky was serene."
message.split('.')
```

Out[134]:

Notice how the character "." was removed and the string was split exactly where the "." was.

Write a function that sums all the numbers in the list and returns that sum.

In [135]:

```
def sumListOfNums(numList):
# Your code here
total = 0
for num in numList:
total += num
return total
```

In [136]:

```
sumListOfNums([1, 4, -2]) # should return 3
```

Out[136]:

In [137]:

```
sumListOfNums([1, 2, 3, 4]) # should return 10
```

Out[137]:

In [138]:

```
sumListOfNums([]) # should return 0
```

Out[138]:

Write a function that prints out the squares of every number from 1 to some number `x`

.

In [139]:

```
def printSquares(x):
# Your code here
for num in range(1, x + 1):
print(num ** 2)
```

In [140]:

```
printSquares(4) # should print 1, 4, 9, 16
```

In [141]:

```
printSquares(6) # should print 1, 4, 9, 16, 25, 36
```

Write a function that prints out all the odd elements in a list.

In [142]:

```
def printOdd(lst):
# Your code here
for i in range(len(lst)):
if i % 2 == 1:
print(lst[i])
```

In [143]:

```
printOdd([4, 'n', 28, 'o']) # should print 'n', 'o'
```

In [144]:

```
printOdd(['a', 'b']) # should print 'b'
```

Write a function that takes a list of numbers and returns the index of the last number greater than 100. If no numbers exist greater than 100, return -1.

In [145]:

```
def lastIndex(numList):
# Your code here
lastIndex = -1
for i in range(len(numList)):
num = numList[i]
if num > 100:
lastIndex = i
return lastIndex
```

In [146]:

```
lastIndex([1, 3, 102, 4]) # should return 2
```

Out[146]:

In [147]:

```
lastIndex([103, 102]) # should return 1
```

Out[147]:

In [148]:

```
lastIndex([1, 2, 3]) # should return -1
```

Out[148]:

Write a function that prints out all the numbers in a list that are both even and at even indices.

In [149]:

```
def evens(numList):
# Your code here
for i in range(len(numList)):
num = numList[i]
if i % 2 == 0 and num % 2 == 0:
print(num)
```

In [150]:

```
evens([0, 4, 5, 7, 8]) # should print 0, 8
```

In [151]:

```
evens([1, 2, 3, 4, 5, 6]) # should not print anything
```

In [152]:

```
evens([2, 3, 4]) # should print 2, 4
```

Write a function that checks whether a string is a palindrome. A palindrome is a word or phrase that reads the same backwards or forwards. The function should return `True`

if it the string is a palindrome and `False`

otherwise.

Note that this is pretty tricky!

In [153]:

```
def isPalindrome(phrase):
# Your code here
for i in range(len(phrase)):
let = phrase[i]
endLet = phrase[len(phrase) - i - 1]
if let != endLet:
return False
return True
```

In [154]:

```
isPalindrome("ADA") # should return True
```

Out[154]:

In [155]:

```
isPalindrome("andy") # should return False
```

Out[155]:

In [156]:

```
isPalindrome("naan") # should return True
```

Out[156]:

In [157]:

```
isPalindrome("turtle") # should return False
```

Out[157]:

In [158]:

```
isPalindrome("able was i ere i saw elba") # should return True
```

Out[158]:

In [159]:

```
isPalindrome("") # should return True
```

Out[159]: