What can sequences do ? List Comprehensions

Select • i-th element: s[i] A cleaner, nicer way to do map-like operations • subsequence (“slice”): s[i:j] >>> [ x*x for x in range(10)] Update -- For mutable sequences (e.g. Lists) [0,1,4,9,16,25,36,49,64,81] • Update i-th element: s[i] = e >>> [2*x for x in “yogurt cheese”] • Update subsequence: s[i:j] = e [“yy”,”oo”,”gg”,”uu”,”rr”,”tt”,…] Member: x in s Iteration: for x in s: map,filter,reduce

List Comprehensions List Comprehensions

Syntax: >>> [ex for x in s] Syntax: >>> [ex for x in s]

Equivalent to: Equivalent to: >>> def map_fn(x): return ex >>> map(map_fn, s)

List Comprehensions List Comprehensions

A cleaner, nicer way to do map+filter-like operations

>>> [ x*x for x in range(10) if even(x)] [0,4,16,36,64] Syntax: >>> [ex for x in s if cx ] >>> [ 2*x for x in “0123456” if even(x)] [“00”,“22”,“44”,“66”] Equivalent to: >>> [z[0] for z in craigslist if z[1]<3.0] [“dinosaur”]

1 List Comprehensions List Comprehensions

Can “nest” the for to iterate over multiple sequences

>>>[(x,y) for x in range(3) for y range(3)]

Syntax: >>> [ex for x in s if cx ] [(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2, 1),(2,2)] Equivalent to: >>>[(x,y) for x in range(3) for y in range(3) if x > y] >>> def map_fn(x): return e x [(1,0),(2,0),(2,1)] >>> def filter_fn(x): return cx >>> map(map_fn, filter(filter_fn, s))

What can sequences do ? Quicksort in Python

Select def sort(L): • i-th element: s[i] if L==[]: return L • subsequence (“slice”): s[i:j] else: Update -- For mutable sequences (e.g. Lists) l=sort(...) • Update i-th element: s[i] = e r=sort(...) • Update subsequence: s[i:j] = e return(l+L[0:1]+r) Member: x in s Iteration: for x in s: map,filter,reduce

Comprehensions: [ex for x in s if cx]

Quicksort in Python Today: Revisit some objects def sort(L): • Exploit features and build powerful if L==[]: return L expressions else: l=sort([x for x in L[1:] if x < L[0]]) Base: int, float, complex r=sort([x for x in L[1:] if x >= L[0]]) return(l+L[0:1]+r) Sequence: string, tuple, list

Maps (Dictionary): key → value

2 Key data structure: Dictionaries Using Dictionaries

Associative arrays, Hash tables … Unsorted list of key,value pairs

Empty Dictionary: {} A table storing a set of “keys”, And a “value” for each key. Non-empty Dictionary: {k1:v1,k2:v2,…}

Any (immutable) object can be a key! Membership: is k in dict: k in

• int,float,string,tuples… Lookup value of key: d[k]

Very useful! Set value of key: d[k]=v

Dictionaries Dictionaries

>>> d={} def freq(s): def plotfreq(s): d={} d=freq(s) >>> d=dict(mexmenu) for in s: for k in d.keys(): >>> d[“ceviche”] = 3.95 if c in d: d[c]+=1 print k, “*”*d[k] >>> d else: d[c]=1 {…} return d

>>> d[“burrito”] >>> d=plotfreq([1,1,3.0,”A”,3.0,”A”,”A”,1,2,3.0,1,”A”]) 3.50 >>> d >>> d.keys() … … >>> d = plotfreq(“avrakedavra”) >>> d.keys() >>> d.values() >>> d …

>>> f = open(“foo.txt”,”read”) >>> f.readlines() … >>> for l in f.readlines():

>>> f.close

You now know enough to do PA5 • Python Tutorial: How to open files, read lines • Use the help command • Document every function: What does it do ?

3 Next: What’s in a name ? What’s in a name ?

More precisely: • How should programmer think of data ML (or Functional Languages) • What does a variable “x” really mean ? • Name refers to a Value • Binding maps Names to Values • Environment list of bindings • Environment can be extended • Environment can’t be changed

Data model in functional PL Data model in OO langs

• Vars = names in phonebook • Variables “point to” objects • Evaluation = Most recent • Environment “frozen” in function value • Objects = boxes with data inside – behavior of function cannot be changed – easier reasoning X “pumpkin”

Y 3.142

Z [1,2,3]

Namespaces Ok, but what IS a namespace ?

• Manage variable names in Python A mapping from names to objects

• Similar to, but different from Environments – Core PL concept, unifies many ideas X “pumpkin” Y 3.142 • We will see very important differences Z [1,2,3]

4 Namespaces vs. Environments 1. Assignment

Both are maps from variables to something Basic operation in Imperative PL:

Namespace Environment x = e

What’s the difference ? 1. Compute object corresponding to e 1. Assignment 2. Change the name “x” to refer to object 2. Updates/Mutation

Simple example Simple example i,s = 0,0 i,s = 0,0 while (i <= 3): while (i <= 3): i,s = i+1, s+i i,s = i+1, s+i

Same name “s” - points to different objects - namespace is not extended

1. Assignment 2. Update/Mutation Change what’s inside the box (object) Basic operation in Imperative PL: - Not with immutable objects x = e - eg. integers - But with mutable objects 1. Compute object corresponding to e - eg. arrays, lists, dictionaries 2. Change the name “x” to refer to object >>> x = [100,200] >>> x How is it different from [100, 200] “build a new box with Assignment: changes box that name refers to >>> x[0] = "gobble gobble" updated value inside” ? >>> x ['gobble gobble', 200]

5 Aliasing Aliasing

Two or more names refer to same object Two or more names refer to same object X

>> x = [100,200] [100,200] “Peter Parker” >> y = x Y

“Spider-Man”

Aliasing and Update Aliasing

Two or more names refer to same object Two or more names refer to same object X X

[‘gobble…’,200] [100,200] [‘gobble…’,200] [100,200] >> x = [100,200] >> x = [100,200] >> y = x Y >> y = x Y

>> y[0] = “gobble gobble" >> y[0] = “gobble gobble" >> x >> x [‘gobble gobble’,200] If multiple names refer to same object, update affects values of all names

Aliasing Aliasing

Does not happen in Ocaml/Functional PLs Good because ? • actually it does happen (where ?) • but not exposed to the (130) programmer

Does happen in every imperative PL Bad because ? • , Python: names point to objects • C: names point to memory cells

6 Namespaces everywhere Creating Namespaces

Namespace = map from names to objects a.py X 22 a x = 22 y = “this sentence is false” “this…” Notion of namespace pervades Python Y

• Can create namespace, b.py X “pumpkin” • Can name a namespace, x = “pumpkin” y = 3.142 • Can peep inside a namespace (see whats bound) Y 3.142 b

>>> import a Go to code! >>> a.x 22

Namespaces Namespaces

For two namespaces a , b: X a X 22 a • names inside unrelated “this…” “this…” Y • names in different spaces Y “22 pumpkins” a.x : attribute/name “x” in space “a” X X “pumpkin” b.x : Y 3.142 3.142 b attribute/name “x” in space “a” Y b

Different names can point to same object! Different names can point to same object!

Creating Namespaces: Fun Calls Creating Namespaces: Fun Calls

x = 10 X 10 x = 10 X 10

def f(y): f def f(y): f y = y + x y = y + x return y y 20 return y y 20

f(x) f(x) f(x) f(x) globals Call-by-Value: globals Questions: • New local namespace for call • Why “new local namespace” (not just stack) ? • y bound to same object (value) as arg x • What’s the deal with “x” not declared/bound in “f” ? • x binding unchanged by call • When do we need to freeze a namespace ? In this case, after call, local namespace disappears…

7 Creating Namespaces: Fun Calls 2 Creating Namespaces: Fun Calls 3

y = 0 len >>> def g(y): x = [10] >>> return y + n y 0 >>> g(5) def f(y): …NameError: z = len(x)+y x 20 global name 'n' return z f is not defined

5 f(5) y z 6 What happened ? Static Scoping f(5) Looks for “n” at run-time ,when “g” is called Lookup at runtime Can’t find “n” in local, global, builtins globals Not compile time Throws run-time error… Missing z added builtins

Creating Namespaces: Fun Calls 3 Aaargh!

>>> def g(y): >>> def g(y): >>> return y + n >>> def return g(y): y + n >>> g(5) >>> g(5) return y + n Changed behavior after definition NameError… NameError… >>> g(5) whether or not fun works depends >>> n = 10 NameError>>> n = 10… on what we did after fundef >>> g(5) >>> g(5)n = 10 15 >>>15 g(5) 15>>> n = 100 Change I/O behavior too … What happened ? >>> g(5) Unlike ML, no new binding: Looks for “n” at run-time ,when “g” is called 105 just change what “n” is bound to Finds “n” in global, returns 15 be careful with free variables! Here “n” is a “free variable” of “g” Needs to be “bound” in some enclosing scope

Python tries to avoid “overwrites” Python tries to avoid “overwrites”

>>> n Python tries to ensure you >>> n Python tries to ensure you 100 don’t overwrite outer variables 100 don’t overwrite outer variables >>> def f(): >>> def f(): >>> n = “smash” >>> n = “smash” >>> print n >>> print n How ? >>> >>> - unlike C/Java >>> f() >>> f() - assignment different from reads smash smash - no variable “declarations” >>> n >>> n 100 100 - assignment = declaration!

8 Python tries to avoid “overwrites” Python tries to avoid “overwrites”

Assignment Revisited >>> n 100 >>> def f(): x = e >>> global n = “smash” >>> print n >>> 1. Compute object corresponding to e >>> f() 2. Change the name “x” to refer to object in smash >>> n the current namespace (added if missing) smash

What happens ? What happens ?

>>> x = 10 >>> x = 10 >>> def g(): >>> def g(): >>> x = x + 1 >>> global x >>> print x >>> x = x + 1 >>> >>> print x >>> g() >>> >>> x >>> g() >>> x

What happens ? What happens ?

>>> x = [10] >>> x = [10] >>> def f(y): >>> def g(): >>> def h(z): >>> x[0] = “abc” >>> return (y+x[0]+z) >>> print x >>> return h >>> >>> >>> g() >>> foo = f(5) >>> x >>> foo

>>> foo(100) >>> 115 >>> foo1 = f(-5) >>> foo1(100) 105

9