List Comprehensions CSE 130 : Fall 2007 Programming Languages 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] Lecture 12: >>> [ 2*x for x in “0123456” if even(x)] What’s in a Name ? [“00”,“22”,“44”,“66”] >>> [z[0] for z in mexmenu if z[1]<3.0] [“taco”,”sopa”,”frijole”] Ranjit Jhala UC San Diego

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

List Comprehensions What can sequences do ?

Select Can “nest” the for to iterate over multiple sequences • i-th element: s[i] • subsequence (“slice”): s[i:j] >>>[(x,y) for x in range(3) for y range(3)] Update -- For mutable sequences (e.g. Lists) [(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2, 1),(2,2)] • Update i-th element: s[i][i] = e >>>[(x,y) for x in range(3) for y in range(#) • Update subsequence: s[i:j] = e if x > y] Member: x in s [(1,0),(2,0),(2,1)] Iteration: for x in s: map,filter,reduce

Comprehensions: [ex for x in s if cx] Syntax: >>> [ex for x in s if cx ]

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

Maps (Dictionary): key → value

1 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 “va lue ” 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 …

News

>>> f = open(“foo.txt”,”read”) • PA #5 will be out tomorrow >>> f.readlines() … >>> for l in f.readlines(): • Quiz next Thu

>>> f.close • William will have OH in Lab tomorrow You now know enough to do PA5 –Instead of Section • Python Tutorial: How to open files, read lines – Quizzes 3,4, (maybe 5) •Use the help command – Bring questions (and code) for NanoML • Document every function: What does it do ?

2 Recap: Python Tutorial Today: What’s in a name ?

•Base Types More precisely: • Sequence Types • How should programmer think of data • Loops • What does a variable “x” really mean ? • Functions • List comprehensions • map, filter, reduce … enough to do PA 5

Today: What’s in a name ? Data model in functional PL

• Vars = names in phonebook • Evaluation = Most recent ML (or Functional Languages) • Environment “frozen” in function value •Namerefers to a Value – behavior of function cannot be changed •Bindingmaps Names to Values – easier reasoning • Environment list of bindings • Environment can be extended • Environment can’t be changed

Data model in OO langs Namespaces

• Variables “point to” objects • Manage variable names in Python

• Objects = boxes with data inside • Similar to, but different from Environments – Core PL concept, unifies many ideas

• We will see very important differences

3 Ok, but what IS a namespace ? Namespaces vs. Environments

A mapping from names to objects Both are maps from variables to something

Namespace Environment

Whats the difference ? 1. Assignment 2. Updates/Mutation

1. Assignment Simple example

Basic operation in Imperative PL: i,s = 0,0 while (i <= 3): i,s = i+1, s+i x = e

1. Compute object corresponding to e 2. Change the name “x” to refer to object 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. tuples, 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]

4 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] [‘gobble…’,200] >> x = [100,200] [100,200] >> x = [100,200] [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

5 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” axa.x : attribute/name “x” in space “a” X X “pumpkin” b.x : Y 3.142 b attribute/name “x” in space “a” Y 3.142 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…

6 Creating Namespaces: Fun Calls 2 Creating Namespaces: Fun Calls 3

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

f(5) y 5 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): >>> return y + n >>> def g(y): Changed behavior after definition >>> g(5) >>> return y + n NameError… >>> g(5) whether or not fun works depends >>> n = 10 NameError… on what we did after fundef >>> g(5) >>> n = 10 15 >>> g(5) Change I/O behavior too … 15 Unlike ML, no new binding: What happened ? >>> n = 100 just change what “n” is bound to Looks for “n” at run-time ,when “g” is called >>> g(5) be careful with free variables! Finds “n” in global, returns 15 105 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 Assignment Revisited 100 Python tries to ensure you >>> def f(): don’t overwrite outer variables x = e >>> n = “smash” >>> print n How ? >>> - unlike C/Java 1. Compute object corresponding to e >>> f() - assignment different from reads smash - no variable “declarations” 2. Change the name “x” to refer to object in >>> n 100 - assignment = declaration! the current namespace (added if missing)

7 Python tries to avoid “overwrites” What happens ?

>>> n >>> x = 10 100 >>> def g(): >>> def f(): >>> x = x + 1 >>> n = “smash” >>> print x >>> print n >>> >>> >>> g() >>> f() >>> x smash >>> n 100

x = 10 def f(): What happened? x = x + 1 What happens ? print x f() >>> x = 10 • You may think it should print 11, and leave >>> def g(): the global unchanged... but here is what >>> global x really happens >>> x = x + 1 >>> print x >>> • Since x is assigned in the function, it treats x >>> g() as a local, and so it adds a binding for it. >>> x • But then it looks up x for evaluating x+1, and it can’t find a local x ⇒ ERROR

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

8