Comp 112

Lecture 6

Lists and References

2017.10.10

Lists

Lists vs Strings

Lists are similar to strings, but more flexible in two ways:

  1. The elements of a list can be of any type, not just characters.

  2. Lists are mutable: they can be changed after being created.

To write a list, use brackets and commas:

[1 , 2 , 3]  #  a list of three integers
[]           #  an empty list

Working with Lists

Homogeneous Lists

List Mutability

Index Assignment

Unlike any type we’ve seen so far, the elements of list type can be changed after being created.

The element located at a list index can be changed using index assignment:

<list> [<index>]  =  <new_element>
xs  =  list ('hello!')
xs [1]  =  'o'
xs [4]  =  'a'

Slice Assignment

A slice can be cut out of a list and a new list spliced into its place using slice assignment:

<list> [<start_index> : <stop_index>]  =  <splice_list>

The list spliced in need not be the same length as the slice cut out.

xs  =  list ('holla!')
xs [1 : 5]  =  ['i' , 'y' , 'a']

Aliasing

Aliasing and Mutability

x  =  [1 , 2 , 3]
y  =  x
z  =  [1 , 2 , 3]

If we make changes to a list using one of its aliases then those changes are visible from any other alias too.

y [1]  =  5
x [1] == 5  and  z [1] == 2    #  evaluates to True

Detecting Aliasing

x = [1 , 2 , 3]
y = x
z = [1 , 2 , 3]
y == x  and  y is x        #  evaluates to True
z == x  and  z is not x    #  evaluates to True

Mutator Functions

def double_effect_free (ns) :
    """
    signature: list (int) -> list (int)
    returns a list containing the doubles of the input list elements
    """
    acc  =  []
    for n in ns :
        acc  +=  [n * 2]
    return acc
def double_by_mutation (ns) :
    """
    signature: list (int) -> NoneType
    effect: doubles every list element in place
    """
    i  =  0
    while i < len (ns) :
        ns [i]  *=  2        #  i.e.  ns [i]  =  ns [i] * 2
        i  +=  1

List Methods

The list namespace contains functions/methods that act on lists, often by mutation. For example:

list.insert (<list> , <index> , <item>)  #  modifies the list by inserting the new item at the specified index
evens  =  [2 , 6]
evens.insert (1 , 4)
evens == [2 , 4 , 6]
list.append (<list> , <item>)  #  modifies the list by adding the new element to the end
evens.append (8)
evens == [2 , 4 , 6 , 8]
list.extend (<list> , <other_list>)  #  modifies the list by appending all the elements of another list
evens.extend ([10 , 12])
evens == [2 , 4 , 6 , 8 , 10 , 12]
list.pop (<list> , <index>)  #  modifies the list by removing the element at the specified index and returning it
ten  =  evens.pop (4)
evens == [2 , 4 , 6 , 8 , 12] and ten == 10

All of these can be easily written in just a few lines using slice assignment.

Generic and Higher-Order Functions

Generic List Operations

Python has a built-in function called “reversed” that does the same thing, except that it returns an iterator.

Digression: Iterators

An iterator is any type that can be iterated over with a for loop. lists are examples of iterators.

It is a quirk of Python that many built-in functions that we would expect to return lists return other iterators instead.

backward_iter = reversed ([1 , 2 , 3])

All we will care about iterators in this course is that they can be coerced to lists.

backward_list = list (reversed ([1 , 2 , 3]))

Mapping over a List

Higher-Order Mapping

Filtering a List

To Do This Week: