0% found this document useful (0 votes)
106 views101 pages

CS1010S-Lec-06 Working With Sequences PDF

The document discusses processing sequences recursively and iteratively. It introduces trees as nested sequences that form hierarchical structures. It provides examples of counting the leaves of a tree recursively by considering the base cases of an empty tree or single leaf node, and the recursive case of summing the leaves of the first subtree and the rest of the tree. Overall, the document discusses common operations on sequences like reversing, scaling, mapping and applying them to trees through recursion.

Uploaded by

Reynard Ardian
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
106 views101 pages

CS1010S-Lec-06 Working With Sequences PDF

The document discusses processing sequences recursively and iteratively. It introduces trees as nested sequences that form hierarchical structures. It provides examples of counting the leaves of a tree recursively by considering the base cases of an empty tree or single leaf node, and the recursive case of summing the leaves of the first subtree and the rest of the tree. Overall, the document discusses common operations on sequences like reversing, scaling, mapping and applying them to trees through recursion.

Uploaded by

Reynard Ardian
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 101

CS1010S Programming Methodology

Lecture 6
Working with Sequences
15th Feb 2023

PollEv.com/nitya
Midterm Exam
• Date. Wed, 1st March 2023
• Time. 16:10 to 17:30
• Venue. MPSH 1(A/B)
• One cheat sheet
- A4/letter size
- Printed or handwritten
- Multicolored

PollEv.com/nitya
PollEv.com/nitya
Midterm Exam
• Scope. Everything including today’s lecture!

• Python Expressions
• Solving Problems with Recursion/Iteration
- Order of Growth
• Higher Order Functions
• Data Abstraction
- Define new Abstract Data Type + Operations
PollEv.com/nitya
Only 20%
Don’t Stress

PollEv.com/nitya
No Tutorials & Recitations
• No Recitations on midterm week
• Tutors will still be at the lab on Mon/Tues
during tutorial times for consultation

PollEv.com/nitya
PollEv.com/nitya
Today’s Agenda
• Processing sequences
- Recursion & Iteration

• Tree as nested sequences


- Hierarchical structures

• Signal-processing view of computations


PollEv.com/nitya
Recap: Data Abstraction
• Abstracts away irrelevant details, exposes
what is necessary
• Separates usage from implementation.
• Captures common programming patterns
• Serves as a building block for other
compound data.

PollEv.com/nitya
Key idea
• Decide on an internal representation of the
Abstract Data Type (ADT) Tuple!
• Write functions that operate on that new
ADT
Key insight: nobody needs to know your internal
representation to use your ADT

PollEv.com/nitya
Sequences
• Sequence is a collection of same type objects
• Sequential data is represented by tuples
• Get the first element of the sequence:
seq[0]
• Get the rest of the elements:
seq[1:]
• If a sequence is a tuple containing a single integer 4:
seq = (4,)
seq[0] à 4
seq[1:] à ()
PollEv.com/nitya
Reversing a Sequence
def reverse(seq):
if seq == ():
return () Recursive
else:
return reverse(seq[1:]) + (seq[0],)

• Notice that (seq[0],) is a tuple and


not an integer
• Can only concatenate tuples with tuples

PollEv.com/nitya
Reverse a sequence
• reverse((1, 2, 3, 4))
• reverse((2, 3, 4)) + (1, )
• reverse((3, 4)) + (2,) + (1, )
• reverse((4, )) + (3, ) + (2, ) + (1, )
• reverse(()) + (4, ) + (3, ) + (2, ) + (1, )
• () + (4, ) + (3, ) + (2, ) + (1, )
• (4, ) + (3, ) + (2, ) + (1, )


(4, 3) + (2, ) + (1, )
(4, 3, 2) + (1, )
Order of growth?
• (4, 3, 2, 1)

PollEv.com/nitya
Visualising Time and Space
• Order of growth
- Time : ! "!
- Space: ! "!

def reverse(seq):
if seq == ():
return ()
else:
return reverse(seq[1:]) + (seq[0],)
PollEv.com/nitya
Key Idea:
Handle the First Element
and then the Rest
Iterate down the sequence!
PollEv.com/nitya
Scaling a sequence
Suppose we want to scale all the elements of a sequence by some
factor
scale_seq((1, 2, 3, 4), 3) à (3, 6, 9, 12)

def scale_seq(seq, factor):


if seq == ():
return ()
else:
return (seq[0] * factor,) +
scale_seq(seq[1:], factor)
Scale the first element of sequence and call recursion on the rest
PollEv.com/nitya
Scaling a sequence
scale_seq((1, 2, 3, 4), 3) à (3, 6, 9, 12)

seq = (1,2,3,4)
(3,) + scale_seq((2,3,4),3)
(3,) + (6,) + scale_seq((3,4),3)
(3,) + (6,) + (9,) + scale_seq((4,),3)
(3,) + (6,) + (9,) + (12,) + scale_seq((),3)
(3,) + (6,) + (9,) + (12,) + ()
(3,) + (6,) + (9,) + (12,)
(3,) + (6,) + (9,12)
(3,) + (6,9,12) Time? !(#2)
(3,6,9,12)
Space? !(#2)
PollEv.com/nitya
Scaling a sequence (iterative)
scale_seq((1, 2, 3, 4), 3) à (3, 6, 9, 12)

def scale_seq(seq, factor): Iterate through each element in


result = () the sequence and scale it.
for element in seq:
result = result + (element * factor,)
return result

Time? !(#2)
Space? !(#)

PollEv.com/nitya
Squaring a sequence
Given a sequence, we want to return a sequence of the squares of all
elements.
square_seq((1, 2, 3, 4)) à (1, 4, 9, 16)

def square_seq(seq):
if seq == ():
return ()
Time? !(#2)
else:
Space? !(#! )
return (seq[0] ** 2, ) +
square_seq(seq[1:])

Homework: Do this iteratively


PollEv.com/nitya
Looking for patterns …
def scale_seq(seq, factor):
if seq == ():
return ()
else:
return (seq[0] * factor,) +
scale_seq(seq[1:], factor)

def square_seq(seq):
if seq == (): Higher-order
return () function!!
else:
return (seq[0] ** 2,) +
square_seq(seq[1:])
PollEv.com/nitya
Mapping
Often, we want to perform the same operation on
every element of a sequence.
( a b c d )
!
(f(a) f(b) f(c) f(d) )

This is called mapping.

PollEv.com/nitya
Mapping
def map(fn, seq):
Note: map() is HOF since fn is a function
if seq == ():
return ()
else:
return (fn(seq[0]), ) + map(fn, seq[1:])

Scaling a list by a factor


def scale_seq(seq, factor):
return map(lambda x: x * factor, seq))

Note: this will overwrite the default Python map function!


PollEv.com/nitya
Examples
map(abs, (-10, 2.5, -11.6, 17))
à (10, 2.5, 11.6, 17)

map(square, (1, 2, 3, 4)) def square(x):


return x**2
à (1, 4, 9, 16)

map(cube, (1, 2, 3, 4)) def cube(x):


return x**3
à (1, 8, 27, 64)

PollEv.com/nitya
Trees

PollEv.com/nitya
Trees
Trees are sequences of sequences and single elements
- This is possible because of the closure property: we can include a
sequence as an element of another sequence
- This allows us to build hierarchical structures, e.g., trees.
((1, 2), (3, 4), 5)

1 2 3 4
PollEv.com/nitya
Examples
((1, 2), 3, 4)
x = ((1, 2), 3, 4)
len(x) à 3
count_leaves(x) à 4 (1, 2) 3 4

(x, x) à (((1, 2), 3, 4), ((1, 2), 3, 4)) 1 2

len((x, x)) à 2

count_leaves((x, x)) à 8

PollEv.com/nitya
How would we count
the leaves?
RECURSION!
PollEv.com/nitya
Recurrence Relation
Observation:
# of leaves of tree

X = X X

# of leaves of # of leaves of
X X first sub-tree + rest of tree
PollEv.com/nitya
Recursion
In other words,
count_leaves(tree) =
count_leaves(tree[0]) +
count_leaves(tree[1:])

PollEv.com/nitya
Base Case:
If tree is empty
Zero!
PollEv.com/nitya
Another Base Case
Observe:
Possible for the head or tail to be a leaf!
Leaf Þ +1

PollEv.com/nitya
Summary
Strategy:
- Base cases:
- Empty tree
- Tree with a single node (leaf)
- Recursive step:
- Current + recursive(rest)

PollEv.com/nitya
Count Leaves
def count_leaves(tree):
if tree == ():
return 0
elif is_leaf(tree):
return 1
else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])

PollEv.com/nitya
What are leaves
Leaf is any object that is not a sequence
Remember type() in Lecture 1:
>>> t = (1, 2, 3)
>>> type(t)
<class 'tuple'>

>>> type(t) == tuple


True

def is_leaf(item):
return type(item) != tuple

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4
count_leaves on
fist subtree
+ count_leaves
on the rest

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

tree
def count_leaves(tree):
is_leaf false
if tree == ():
5 return 0
elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
elif is_leaf(tree):
tree return 1
is_leaf false 1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
tree + count_leaves(tree[1:])
is_leaf true 3 2 4
count_leaves
+1

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 tree 4
is_leaf true

count_leaves count_leaves
+1 +1

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4
count_leaves

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4
count_leaves + count_leaves

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 1 elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4
count_leaves

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 1 elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4
count_leaves + count_leaves

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 1 elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4
count_leaves

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
1 if tree == ():
5 return 0
2 1 1 elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 1 1 elif is_leaf(tree):
return 1
1 + else:
1 return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 1 elif is_leaf(tree):
return 1
+ 1 else:
2 return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4

PollEv.com/nitya
How count_leaves works
tree = ((3, 2), 1, (4,), 5)

def count_leaves(tree):
if tree == ():
5 return 0
2 + 3 elif is_leaf(tree):
return 1
1 else:
return count_leaves(tree[0])
+ count_leaves(tree[1:])
3 2 4

PollEv.com/nitya
How would we scale
each leaves by a factor?
RECURSION!
PollEv.com/nitya
Mapping over trees
Suppose we want to scale each leaf by a factor, i.e.

mytree à (1, (2, (3, 4), 5), (6, 7))

scale_tree(mytree, 10)
à (10, (20, (30, 40), 50), (60, 70))

PollEv.com/nitya
Strategy
• Since tree is a sequence of sequences, we can map
over each element in a tree.
• Each element is a subtree, which we recursively scale,
and return sequence of results.
• Base case: if tree is a leaf, multiply by factor

PollEv.com/nitya
Mapping over trees
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor * subtree
else:
return scale_tree(subtree, factor)
return map(scale_func, tree)

Calls map() which applies scale_func on each element of the tree

def map(fn, seq):


if seq == ():
return ()
else:
return (fn(seq[0]), ) + map(fn, seq[1:])
PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)

def scale_tree(tree, factor):


def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

3 2 4

Suppose we do scale_tree(tree, 2)
PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
tree def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

3 2 4 def map(fn, seq):


if seq == ():
return ()
else:
PollEv.com/nitya
return (fn(seq[0]), ) + map(fn, seq[1:])
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
subtree
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

3 2 4 def map(fn, seq):


if seq == ():
return ()
else:
PollEv.com/nitya
return (fn(seq[0]), ) + map(fn, seq[1:])
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
subtree
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

3 2 4 def map(fn, seq):


Not a leaf if seq == ():
return ()
else:
PollEv.com/nitya
return (fn(seq[0]), ) + map(fn, seq[1:])
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
subtree
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

3 2 4 def map(fn, seq):


if seq == ():
return ()
else:
PollEv.com/nitya
return (fn(seq[0]), ) + map(fn, seq[1:])
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
tree = subtree return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

3 2 4 def map(fn, seq):


if seq == ():
return ()
else:
PollEv.com/nitya
return (fn(seq[0]), ) + map(fn, seq[1:])
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

subtree 3 2 4 def map(fn, seq):


if seq == ():
return ()
else:
PollEv.com/nitya
return (fn(seq[0]), ) + map(fn, seq[1:])
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)

def scale_tree(tree, factor):


def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

3 2 4
Is a leaf!

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)

def scale_tree(tree, factor):


def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

6 2 4

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)

def scale_tree(tree, factor):


def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

6 2 4
Is a leaf!

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)

def scale_tree(tree, factor):


def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

6 4 4
Is a leaf!

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
1 factor)
return map(scale_func, tree)

6 4 4
Is a leaf!

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
2 factor)
return map(scale_func, tree)

6 4 4

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
2 factor)
return map(scale_func, tree)

6 4 4
Not a leaf!

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
2 factor)
return map(scale_func, tree)

6 4 4

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
2 factor)
return map(scale_func, tree)

6 4 4
Is a leaf!

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:
return scale_tree(subtree,
2 factor)
return map(scale_func, tree)

6 4 8

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Apply scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
5 else:

2 Is a leaf! return scale_tree(subtree,


factor)
return map(scale_func, tree)

6 4 8

PollEv.com/nitya
Let’s see what scale_tree does
tree = ((3, 2), 1, (4,), 5)
Done applying scale_func to each element
def scale_tree(tree, factor):
def scale_func(subtree):
if is_leaf(subtree):
return factor*subtree
10 else:
return scale_tree(subtree,
2 factor)
return map(scale_func, tree)

6 4 8

PollEv.com/nitya
scale_tree without using map
def scale_tree(tree,factor):
if tree == ():
return ()
elif is_leaf(tree):
return tree*factor
else:
return (scale_tree(tree[0],factor),) + scale_tree(tree[1:],factor)

Compare
def count_leaves(tree):
if tree == ():
return 0
elif is_leaf(tree):
return 1
else:
return count_leaves(tree[0]) + count_leaves(tree[1:])
PollEv.com/nitya
Key Idea:
Traverse tree with
recursion
Check for leaf!

PollEv.com/nitya
Sanity Check (QOTD)
How do you write a function copy_tree that takes a tree
and returns a copy of that tree?

def copy_tree(tree):
return tree # is NOT acceptable!

>>> t = (1, 2, 3)
>>> t_copy = copy_tree(t)
t == t_copy à True
t is t_copy à False
PollEv.com/nitya
Signal Processing View

PollEv.com/nitya
Listening to Music
• Signal goes through various stages of processing.
• Additional component can be inserted.
• Easy to change component.
• Components interface via signals.

Producer Amplifier Consumer


Filter Converter
Modeling Computation as Signal Processing
• Producer (enumerator) creates signal.
• Filter removes some elements.
• Mapper modifies signal.
• Consumer (accumulator) consumes signal.

PollEv.com/nitya
Benefits
1. Modularity: each component
independent of others; components may
be re-used.
2. Clarity: separates data from processes
3. Flexibility: new component can be added

PollEv.com/nitya
Example: Sum of squares of odd leaves
Given a tree, we want to add the squares of
only those leaves that contain odd number.

sum_odd_squares(((1, 2), (3, 4))) à 10

• If tree is empty, return empty tree


• If the tree is an odd leaf node return the square of the leaf node else
return 0
• Else do sum of squares on the first tree + sum of squares on the rest

PollEv.com/nitya
Example: Sum of squares of odd leaves
def sum_odd_squares(tree):
if tree == ():
return 0
elif is_leaf(tree):
if tree % 2 == 0:
If leaf:
return 0 • If leaf is even -> return 0,
• Else square the leaf node and return
else:
return tree ** 2
else:
return sum_odd_squares(tree[0]) +
sum_odd_squares(tree[1:])
PollEv.com/nitya
Alternative Approach
View it as signal processing computation!
Enumerate Filter Map Accumulate
tree leaves odd? square +, 0

How to represent “signals”?


- Sequences

PollEv.com/nitya
Enumerating leaves
What does the following function do?

def enumerate_tree(tree):
if tree == ():
return ()
elif is_leaf(tree):
return (tree,)
else:
return enumerate_tree(tree[0]) +
enumerate_tree(tree[1:])

enumerate_tree((1, (2, (3, 4)), 5)) à (1, 2, 3, 4, 5)


also known as flattening the tree.

PollEv.com/nitya
Filtering a sequence
def filter(pred, seq):
if seq == ():
return () If predicate is true, include seq[0]
elif pred(seq[0]): and apply recursion on rest
return (seq[0],) + filter(pred, seq[1:])
else:
If predicate is false, do not include
return filter(pred, seq[1:])
seq[0] and apply recursion on rest

is_odd = lambda x: x%2 != 0


filter(is_odd, (1, 2, 3, 4, 5)) à (1, 3, 5)

Note: we are overwriting the default Python filter function!


PollEv.com/nitya
Accumulating a sequence
def accumulate(fn, initial, seq): e.g.,
if seq == (): 1+accumulate(fn, 0, (2,3,4,5))
1+2+accumulate(fn, 0, (3,4,5))
return initial 1+2+3+accumulate(fn, 0, (4,5))
else: 1+2+3+4+accumulate(fn, 0, (5))
return fn(seq[0], 1+2+3+4+5+accumulate(fn, 0, ())
1+2+3+4+5+0
accumulate(fn, initial, 1+2+3+4+5
seq[1:])) 1+2+3+9
1+2+12
add = lambda x, y: x+y 1+14
accumulate(add, 0, (1, 2, 3, 4, 5)) à 15 15
accumulate(lambda x, y:(x, y), (), (1, 2, 3, 4, 5))
à (1, (2, (3, (4, (5, ())))))

PollEv.com/nitya
Putting it together
def sum_odd_squares(tree):
accumulate(add, 0,
map(square,
filter(is_odd,
enumerate_tree(tree))))

Enumerate: Filter: Map: Accumulate:


tree leaves is_odd square add, 0

PollEv.com/nitya
Putting it together
(1, 2, (3, 4))
enumerate_leaves
(1, 2, 3, 4)
filter odd?
(1, 3)
map square
(1, 9)
accumulate add , 0
10

PollEv.com/nitya
Another Example:
Tuple of even Fib
Want a list of even fib(k) for
all k up to given integer n.

PollEv.com/nitya
“Usual” Way
def even_fibs(n):
result = ()
for k in range(1, n + 1):
f = fib(k)
if is_even(f):
result = result + (f, )
return result

is_even = lambda x: x % 2 == 0

>>> even_fibs(30)
(2, 8, 34, 144, 610, 2584, 10946, 46368, 196418, 832040)

PollEv.com/nitya
Signal processing view
• Even fibs
Accumulate:
Enumerate: Map: Filter:
lambda x,y:
integers fib is_even
(x,y), ()

• Compare: sum square odd leaves


Enumerate: Filter: Map: Accumulate:
tree leaves is_odd square add, 0

PollEv.com/nitya
Enumerate integers
def enumerate_interval(low, high):
return tuple(range(low,high+1))

enumerate_interval(2, 7)
à (2, 3, 4, 5, 6, 7)

PollEv.com/nitya
Even Fibs
def even_fibs(n):
accumulate(lambda x,y: (x,y), (),
filter(is_even,
map(fib,
enumerate_interval(1, n))))

Accumulate:
Enumerate: Map: Filter:
lambda x,y:
integers fib is_even
(x,y), ()

PollEv.com/nitya
Putting it together
(1, 10)
enumerate_interval
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
map fib
(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
filter even
(2, 8, 34)
accumulate (x,y) , ()
(2, (8, (34, ())))

PollEv.com/nitya
Signal Processing View
• Modular components:
- Enumerate, Filter, Map, Accumulate
- Each is independent of others.
- Modularity is a powerful strategy for controlling
complexity.

PollEv.com/nitya
Signal Processing View
• Build a library of components.
• Sequences used to interface between
components.

PollEv.com/nitya
Re-using components
• Want a sequence of squares of fib(k)

def list_fib_squares(n):
return accumulate(lambda x,y: (x, y), (),
map(square,
map(fib,
enumerate_interval(1, n))

PollEv.com/nitya
Other Uses
• Suppose we have a sequence of personnel records.
• Want to find salary of highest-paid programmer.

def salary_of_highest_paid_programmer(records):
return accumulate(max, 0,
map(salary,
filter(is_programmer, records)))

PollEv.com/nitya
Default Python map and filter functions
Returns an iterable instead of tuple, but you can force it into a tuple.

>>> a = (1,2,3,4,5)
>>> b = filter(lambda x: x%2 == 0, a)
>>> b
<filter object at 0x02EC4710>

PollEv.com/nitya
>>> for i in b:
print(i)
2
4

>>> c = tuple(b)
>>> c
()

PollEv.com/nitya
>>> b = filter(lambda x: x%2 == 0, a)
>>> b
<filter object at 0x02E42C10>

>>> c = tuple(b)
>>> c
(2, 4)

>>> for i in c:
print(i)
2
4
PollEv.com/nitya
Summary
• Data often comes in the form of sequences
- Easy to manipulate using recursion/iteration
- Can be nested
• Closure property allows us to build hierarchical
structures, e.g. trees, with tuples
- Can use recursion to traverse such structures

PollEv.com/nitya
Summary
• “Signal-processing” view of computation.
- Powerful way to organize computation.
- Sequences as interfaces
- Components: (i) Enumeration, (ii) Map, (iii) Filter, (iv)
Accumulate

PollEv.com/nitya

You might also like