2018.04.03

# Tuples vs Lists

• Recall that a homogeneous list has fixed type but variable length:

``beatles = ["John" , "Paul" , "George" , "Ringo"]  #  signature: list (str)``
• In contrast, a tuple has fixed size but each field can have a different type:

``john = ("Lennon" , "John" , 1940)  #  signature: tuple (str , str , int)``
• Tuples are one way to collect related data into one object. (We’ll meet other ways soon.)

• A tuple is written like a list, but without the brackets.

Often parentheses are needed to mark the start/end of a tuple.

• Unlike lists, tuples are immutable.

• In our signatures for tuples, we’ll include the signature for each of its fields.

# Tuple Indexing and Slicing

``john = ("Lennon" , "John" , 1940)  #  signature: tuple (str , str , int)``
• Like lists, tuples support indexing:

``````last = john 
first = john 
born = john ``````
• …and also slicing: a slice of a tuple is another tuple:

``name = john [0 : 2]  #  signature: tuple (str , str)``
• Because they are immutable, tuples don’t support index assignment or slice assignment.

# Returning Multiple Values

• Functions may take any number of arguments, but must return exactly one result.

• With tuples we can simulate returning multiple results:

``````def quot_rem (x , y) :
"""
signature:  int , int -> tuple (int , int)
precondition:  y != 0
returns the quotient and remainder of dividing x by y
"""
return (x // y , x % y)``````

# Tuple Assignment

• By forming tuples of variables, we can perform multiple assignments at once.

``(x_coord , y_coord) = (4 , 2)``
• This is useful for deconstructing a tuple into its parts:

``(last , first) = john [0 : 2]``
``(quotient , remainder) = quot_rem (7 , 3)``
• We can use tuple assignment to swap the values of variables:

``````import random

(x , y) = (random.randint (1 , 100) , random.randint (1 , 100))
if x > y :
(x , y) = (y , x)
# postcondition:  x <= y``````

# Enumerating a List

Sometimes we want to iterate over a list without controlling the order, but we still want access to the indices:

``````def indices (needle , haystack) :    # from homework 5
"""
signature : any a . a , list (a) -> list (int)
returns the list of matching indices
"""
acc = []
i = 0
while i < len (haystack) :
if needle == haystack [i] :
acc.append (i)
i += 1
return acc``````

There is a built-in function called `enumerate` with signature `any a . iterator a -> iterator (tuple (int , a))` that packages up each list entry with its index:

``list (enumerate (['a' , 'b' , 'c']))  ==  [(0 ,'a') , (1 ,'b') , (2 ,'c')]``

Letting us write:

``````def indices (needle , haystack) :
acc = []
for (index , item) in enumerate (haystack) :    #  note the tuple assignment
if needle == item :
acc.append (index)
return acc``````

# Spreadsheets

• A homogeneous list of tuples can act as a table or spreadsheet:

``````#signature:  list (tuple (str , str , int))
beatle_grades = \
[   #  last      ,  first    , grade
("Lennon"    , "John"   , 94) ,
("McCartney" , "Paul"   , 82) ,
("Harrison"  , "George" , 79) ,
("Starr"     , "Ringo"  , 66)
]``````
• We can use Python to write spreadsheet functions:

``````def get_grade (record) :
return record ``````
``````def average_grade (records) :
# signature:  list (tuple (str , str , int)) -> float
# precondition:  records != []
return sum (map (get_grade , records)) / len (records)``````

# Lists as Lookup Tables

One way to think of a list: is as a look-up table:

index value
0 `"John"`
1 `"Paul"`
2 `"George"`
3 `"Ringo"`

From this perspective, the table’s look-up indices are the numbers from `0` to `len(xs)-1`.

But there is no reason why a table’s look-up indices need to be these particular numbers.

index value
0 `"N/A"`
1 `"N/A"`
1895 `"N/A"`
1896 `"Thomas Burke"`
1897 `"N/A"`
1898 `"N/A"`
1899 `"N/A"`
1900 `"Frank Jarvis"`
1901 `"N/A"`
2015 `"N/A"`
2016 `"Usain Bolt"`

…or even numbers at all.

# Dictionaries

• Python has a look-up table type where the indices can be values of any immutable type. It is called a dictionary.

• An index of a dictionary is called a key.

• The item in a dictionary indexed by a key is called its value.

• For lookup, dictionaries support indexing (“`_[_]`”), just like lists.

• For assignment, dictionaries support index assignment, just like lists.

• For deleting an entry from a dictionary, there is a `del` operator: “`del <dictionary> [<key>]`”.

• The “`dict`” function creates a new empty dictionary.

``````olympic_mens_100 = dict ()                # signature: dict (int -> str)
olympic_mens_100  = 'Thomas Burke'  # ...
olympic_mens_100  = 'Ben Johnson'
del olympic_mens_100                # revoked due to doping
olympic_mens_100  = 'Carl Lewis'    # ...
olympic_mens_100  = 'Usain Bolt'
olympic_mens_100  = 'Usain Bolt'
olympic_mens_100  = 'Usain Bolt'``````

Our signatures for dictionaries include the signatures for the keys and values.

# Dictionary Notation

• Similar to the way we can write lists using bracket notation:

``['John' , 'Paul' , 'George' , 'Ringo']              # signature: list (str)``
• We can write dictionaries using brace notation:

``{1809:'Thomas Burke' , ⋯ , 2016:'Usain Bolt'}  # signature: dictionary (int -> str)``
• Each key-value pair is written:

``<key> : <value>``
• The key-value pairs are separated by commas, just like list elements.

• If a key is repeated, its last value will overwrite any earlier ones.

# Keys and Values

• The dictionary `keys` method returns the keys of a dictionary:

``om100_keys = olympic_mens_100.keys ()``
• The dictionary `values` method return the values (with repetitions) stored in a dictionary:

``om100_vals = olympic_mens_100.values ()``
• The dictionary `items` method returns the key-value pairs of a dictionary as tuples:

``om100_items = olympic_mens_100.items ()``

The objects returned by these methods are list-like iterators.

If you need an actual list then coerce them with the `list` function:

``om100_key_list = list (olympic_mens_100.keys ())``

# Iterating Over a Dictionary

• We can iterate over the object returned by the dictionary `keys` method:

``````for key in olympic_mens_100.keys () :
print (str (key) + ": " + olympic_mens_100 [key])``````
• As a shorthand, we can iterate over the dictionary object itself:

``````for key in olympic_mens_100 :
print (str (key) + ": " + olympic_mens_100 [key])``````
• Or we can iterate over the key-value pairs returned by the dictionary `items` method:

``````for (key , val) in olympic_mens_100.items () :
print (str (key) + ": " + val)``````

# Key Sorting

Recall that there is a function called `sorted` that returns a sorted (according to “`_<=_`”) version of a list (or iterator):

To sort our olympic records chronologically:

``````for key in sorted (olympic_mens_100.keys ()) :
print (str (key) + ": " + olympic_mens_100 [key])``````

or:

``````for (key , val) in sorted (olympic_mens_100.items ()) :
print (str (key) + ": " + val)``````

### Key Testing

• The “`_in_`” and “`_not in_`” operators are overloaded to work with dictionaries.

• They test for the presence of keys (but not values).

``````2016 in olympic_mens_100          #  evaluates to True
"Usain Bolt" in olympic_mens_100  #  evaluates to False``````

# Example: Letter Counting

``````def letter_counts (text) :
# signature:  str -> dictionary (str -> int)
counts = dict ()                     # initialize the dictionary
for character in text.lower () :     # count the letter occurrences
if str.isalpha (character) :
if character not in counts :
counts [character] = 0
counts [character] += 1
return counts``````
``````def print_dict (dictionary) :
# signature:  any a . dict (str -> a) -> NoneType
# prints sorted contents of the dictionary
for (key , val) in sorted (dictionary.items ()) :
print (key + ": " + str (val))``````
``````two_cities = "It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, \
it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, \
it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us..."``````
``print_dict (letter_counts (two_cities))``

# To Do This Week:

• Reading:

• sections 11.1–11.5: dictionaries (6 pages)
• sections 12.1–12.3 , 12.5–12.7: tuples (6 pages)
• Come to lab on Thursday.

• Complete homework 10.