245
CS 152 - Lecture 1 Cay S. Horstmann

CS 152 - Lecture 1horstmann.com/sjsu/spring2018/cs152/midterm/slides.pdf · 2018-03-12 · CS 152 - Lecture 1 Cay S. Horstmann. This document was created with Prince, a great way

  • Upload
    others

  • View
    10

  • Download
    0

Embed Size (px)

Citation preview

CS 152 - Lecture 1

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Course Objectives

▪ Objectives:• To ensure that students gain an understanding of programming language

design and language translation.• To achieve competence in a functional programming language.

▪ What will you learn?• How a programming language is compiled or interpreted• How to program in paradigms other than OO

▪ Why should you care?• For most of your career, you will program in a language other than Java or

C...• ...and none of us knows today what that language is

Java.* is not the End of History

▪ 1960: Fortran, Algol, Lisp (Datesapproximate)

▪ 1980: C▪ 1990: C++▪ 1995: Java, JavaScript▪ Java is showing its age• Not “agile”• Weak support for concurrency

▪ Atwood's law: "Any application that canbe written in JavaScript, will eventuallybe written in JavaScript."

The “Cambrian Explosion” of Languages

▪ Lots of programming languages comeand go:

• Ruby, Groovy• Python, PHP, Perl• Lisp, Scheme, ML, Haskell• Smalltalk• C#, F#• Matlab, R, Julia• Swift, Kotlin• Rust• Go

▪ Some are optimized for different tasks(UI, scientific programming, scripting, ...)

▪ Some aim to be your next general purpose language

What You Will Learn

▪ A functional subset of Java (instead ofScheme/ML/Haskell)

▪ The Scala combinator parser library(instead of JavaCC/Antlr/yacc)

▪ JVM code generation▪ Scheme, functional JavaScript

▪ Prolog▪ Project: Web development with alternative languages

What You Need to Succeed

Prerequisites

▪ CS151: Interfaces, inner classes,generic types

▪ CS46A/B: Unix shell, basic scripting

Time

▪ You can't expect to learn complexskills just by listening to lectures

▪ Spend 9 hours per week for this class• 2.5 hours in class

• 6.5 hours (!) outside class for preparation and homework

Laptop

▪ Bring it to each class meeting

▪ Install Java 8 and the Scala IDE▪ Be able to run a Bash shell

Questions

▪ Ask questions right away when you arestuck

▪ Use the Piazza online discussiongroup.

▪ You'll get a better grade if you ask lotsof questions.

▪ Answer your peers' questions. You'llget a better grade if you do.

▪ Private or confidential questions—email or office hours

Plagiarism and Cheating

▪ When you submit work, it must be yourown personal, individual intellectualcreation

• Not that of your “study group”

▪ Can you copy and paste?• Always ok to copy• Pasting is the problem ☺

• Ok to use code from the course materials without attribution• Otherwise, you must attribute the source

▪ Don't post/email working code• You don't know what the recipient will do with it• Sending someone a solution is cheating

▪ I periodically run a plagiarism checker

• If I find cheating, I don't play Sherlock Holmes to find who wrote it—I reportboth

▪ I report everyone whom I find cheating to Student Conduct• I have to• And I will recommend that an appropriate penalty is for you to fail the course

Git

▪ You submit all class work via Git▪ Make a private repo on BitBucket or

GitHub• If you use your SJSU email address, BitBucket

gives you a free account with unlimited privaterepos

▪ Add me as a contributor to your repo.My BitBucket and GitHub ids are

cayhorstmann▪ Use a single repo for all homework and exams▪ Make folders hw1, hw2, lab1, exam1, ...▪ Commit at least once a day for homework/every 10 min in an

exam

▪ Push before midnight on due date. Grace period until 6am nextmorning. No mercy afterwards.

▪ Never used Git? Check out parts A and D in this CS46B lab.

Adding

▪ Do homework 1 and send me youranswers.

▪ I will send you an add code whenspace becomes available. Use itwithin 24 hours, or it will become invalid

▪ Show up for all classes/labs even if youhaven't received your code

Things To Do Today

▪ Log into Piazza▪ Install Java 8, Scala, and Git on your

laptop▪ If you run Windows and don't know

your way around the Windows 10subsystem for Linux, or if you runMacOS and don't know how to navigatethe difference between Linux and BSD,install VirtualBox and a Linux VM.

▪ Complete Homework 1 and send me your Git repo URL▪ Important: In lieu of a roll call today, you must turn in homework

1, or I will drop you from this course for lack of presence/prerequisites.

Lab

▪ Getting going with Piazza▪ Getting going with Git▪ Looking at the homework assignment

Lecture 1 Clicker Question 1

What happens with this code?

List<String> strings = new ArrayList<>(Arrays.asList("Harry", "Sally"));List<Object> objects = strings;objects.set(0, new Integer(42));String first = strings.get(0);

1. The code compiles and runs without errors2. The code throws an exception in line 33. The code throws an exception in line 44. The code doesn't compile

Lecture 1 Clicker Question 2

What happens with this code?

List<String> strings = new ArrayList<>(Arrays.asList("Harry", "Sally"));List objects = strings;objects.set(0, new Integer(42));String first = strings.get(0);

1. The code compiles and runs without errors2. The code throws an exception in line 33. The code throws an exception in line 44. The code doesn't compile

Lecture 1 Clicker Question 3

What happens with this code?

String[] strings = { "Harry", "Sally" };Object[] objects = strings;objects[0] = new Integer(42);String first = strings[0];

1. The code compiles and runs without errors2. The code throws an exception in line 33. The code throws an exception in line 44. The code doesn't compile

Installing Git

▪ Make an account on BitBucket. Use your SJSU email address.▪ Make a repository on BitBucket. Be sure it is a private repo. Call itcs152. Add me (cayhorstmann) as a collaborator with Write access(in Settings → Access Management → Users).

▪ Start a Bash shell.▪ Make sure you have git:

git --version

It doesn't matter what version you have. If you don't have git, use

sudo apt-get install git

▪ Clone the repo:

git clone ssh://[email protected]/yourname/cs152.git ~/cs152

Adding to the Repository

1. In the cs152 subdirectory that was created by git clone, make a subdirectoryhw1

2. In that directory, make a file aboutme.txt as described in Homework 13. Open a terminal and change to the directory into which you cloned the repo:

cd cs152

4. Type

git add hw1/aboutme.txt

5. Type

git commit -a -m "Started homework 1 "

6. Type

git push origin master

7. In the BitBucket web interface, locate hw1/aboutme.txt8. Did you find it? Hooray—you have just reached level 2.

A UML Diagram

▪ Get together with two buddies▪ Read through homework 1▪ Grab a marker pen and step up to a whiteboard▪ Draw a UML diagram that shows the relationships between

Expr<Integer>Const<Integer>Op<Integer>Function<Integer>Sum

CS 152 - Lecture 2

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Functional Programming

▪ Functional programming: Functions are values▪ In Java, values are

• Primitive types int, double, etc.• References to objects or arrays

▪ A method is not a “first class” value in Java• Cannot declare a variable that holds a method• Cannot create new method in a running program

▪ In a functional programming language, functions are first-classvalues

• Can have variables that hold functions• Can create new functions

Functional Programming in Scala

▪ Functions can be values

val num = 3.14val fun = math.ceil _fun(num) // prints 4

▪ Functions can be anonymous...

(x : Int) => x * x

▪ ...just like numbers

3.14

▪ Of course, can put function values into variables and then usethem

val square = (x : Int) => x * xsquare(10) // prints 100

▪ ...again, just like numbers

val pi = 3.14pi * 10 // prints 31.4

Why Functional Programming?

▪ Simpler and clearer programming style▪ Often useful to pass functions as parameters

val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)numbers.map((x : Int) => x * x)

// Yields a new List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

▪ How would you write a generic map function in Java that does thesame?

▪ You can pass behavior as an interface:

public static <T> List<R> map(List<T> values, Transformer<T, R> f)

▪ Java 8 lambdas provide syntactic sugar, but they are not truefunctions.

▪ Functions have been with us for hundreds of years ofmathematics.

▪ Making them “first class citizens” in a programming languageprovides expressive power.

Scala Basics

▪ Types Int, Double, Boolean, String▪ Arithmetic like in Java: + - * / %▪ Variable type is inferred:

val luckyNumber = 13 // luckyNumber is an Int

▪ Function types:

val square = (x : Int) => x * x // square is an Int => Int

▪ Semicolons at the end of a line are optional

val x = 1val y = 2 + // end line with operator to indicate that there is more to come3

▪ Everything is an object

42.toHexString

▪ Methods without parameters don't require ()

Immutability

▪ Immutable: Cannot change▪ In Java, strings are immutable

• "Hello".toUpper doesn't change "Hello" but returns a new string "HELLO"

▪ In Scala, val are immutable

val num = 3.14num = 1.42 // Error

▪ Pure functional programming: No mutations▪ If you call a function twice with the same inputs, you know you'll

get the same result▪ Functions that don't mutate state are inherently parallelizable▪ Important consideration in light of the end of Moore's Law

If/Else

▪ if (booleanExpression) expression1 else expression2

▪ if/else is an expression, not a statement. Can be used in otherexpressions:

val x = (if (true) 2 else 4) * 3

▪ Like ? : in Java▪ Type is the most specific supertype common to expression1,

expression2

val x = if (true) 3 else "Hello" // type is AnyVal

▪ Omitting the else yields type Unit (like void in Java); not useful infunctional programming

Recursion

▪ def syntax for functions

def triple(x : Int) = 3 * x// same as val triple = (x : Int) => 3 * x

▪ With recursive functions, also need to specify return type:

def fac(x : Int) : Int = if (x == 0) 1 else x * fac(x - 1)

▪ Need def because the name is used on the right

val fac = (x : Int) => if (x == 0) 1 else x * fac(x - 1) // fac not defined yet

▪ Iteration (while, for) can always be expressed as recursion

To iterate is human; to recurse, divine (L. Peter Deutsch)

Lab

▪ Format of classes: approx. 20 minuteslecture, 45 minutes lab, 10 minutewrap-up

▪ You work with a buddy▪ One of you (the coder) writes the code,

the other (the scribe) types up answers▪ When you get stuck, ask your buddy first!▪ Switch coder/scribe roles each lab▪ The scribe submits lab work in lab1/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: The Scala Interpreter

1. Start Eclipse and make a new Scala project: File -> New -> Project -> ScalaProject. Give a name lab1 and click Finish. Right-click on the project in thePackage Explorer, then select New -> Scala worksheet. Call it sheet1.

2. Type 39+3 and save the worksheet. What do you get?3. Type val a = 39 + 3. What do you get?4. Type a + 1. What do you get?5. Type a = 9. What do you get? Why?6. Type val b; (This time with a semicolon.) What do you get? Why?

Step 2: Functions are Values

1. Type val triple = (x : Int) => 3 * x. What do you get?2. Type triple(5). What do you get?

HINT: These “What do you get” exercises are a lot more effective when you andyour buddy first discuss what you think you'll get. “Form a hypothesis” is an essentialpart of the learning path.

3. Type triple. What do you get?4. What is the type of triple in Scala?5. What is the type of 5 in Scala?

Step 3: Functions as Parameters

1. Type List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10). What do you get?2. Type List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).map(triple). What do you get?

Why?3. How do you get the cubes of the numbers from 1 to 10 without using val or

def? Hint: Anonymous function.

Step 4: Simple Recursion

1. Your task is to write a function sevens(n: Int): Int that counts how manydigits of n are the digit 7. For example, sevens(747) returns 2. How would youdo this in Java (without converting the number to a string)?

2. In functional programming, you can't increment a counter. But you can userecursion. How can you compute the answer recursively? Hint: If n is 0, youknow the answer. Otherwise, in plain English or pseudocode, how can youcompute the answer from n % 10 and sevens(n / 10)?

3. What's your code in Scala?

CS 152 - Lecture 3

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Blocks

▪ In Scala, a block is an expression with a value.

val distance = {val dx = x2 - x1val dy = y2 - y1math.sqrt(dx * dx + dy * dy)

}

▪ The last expression in the block is the value of the block.▪ Common in complex recursions.

def digitsum(n: Int): Int = if (n == 0) 0 else {val last = n % 10val rest = n / 10last + digitsum(rest)

}

Local Functions

▪ You can define a function inside another function:

def distance(x1: Int, x2: Int, y1: Int, y2: Int) = {def sq(x: Int) = x * xmath.sqrt(sq(x2 - x1) + sq(y2 - y1))

}

▪ Useful for recursive helper functions.

def digitsum(n: Int) = {@tailrec def digitsumHelper(n: Int, sum: Int): Int = if (n == 0) sum else

digitsumHelper(n / 10, sum + n % 10)digitsumHelper(n, 0)

}

▪ @tailrec tells Scala that you believe that the recursion can berewritten as a loop.

def digitsumHelper(n: Int, sum: Int): Int = {start:

if (n == 0) sum else {n = n / 10sum = sum + n % 10goto start

}}

Lists

▪ Very different from Java linked lists. No iterators▪ Three primitives: head, tail, :: (pronounced cons)▪ A list is either empty (Nil) or has a head and tail

val lst = List(1, 4, 9)lst.head // 1lst.tail // List(4, 9)lst.tail.tail.tail // Nil

▪ Use :: to build lists

0 :: lst // List(0, 1, 4, 9)

List Functions

▪ Use recursion for list functions

def sum(lst : List[Int]) : Int =if (lst.isEmpty)

0 elselst.head + sum(lst.tail)

▪ Use :: to recursively build lists

def squares(n : Int) : List[Int] =if (n == 0)

List(0) elsen * n :: squares(n - 1)

Why This Isn't Inefficient

▪ lst.tail doesn't make a new list—it is a reference to the tail cell

▪ Works because lists are immutable

More Recursion Examples

▪ def append(a: List[Int], b: List[Int]): List[Int] = if (a.isEmpty) b elsea.head :: append(a.tail, b)

▪ def reverse(a: List[Int]): List[Int] = if (a.isEmpty) a elseappend(reverse(a.tail), List(a.head))

▪ reverse((1 to 1000000).toList)(1 to 1000000).toList.reverse

▪ def reverse(a: List[Int]) = {@tailrec def reverseHelper(a: List[Int], reversed: List[Int]): List[Int] =

if (a.isEmpty) reversed else reverseHelper(a.tail, a.head :: reversed)reverseHelper(a, Nil)

}

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab2/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: Lists

1. What is the type of 1 :: 2 :: 3 :: Nil? (Just ask the Scala interpreter)2. What is the type of 1 :: "Hello" :: Nil?3. What happens when you evaluate the expression 1 :: 2 :: 3? Why?4. How do you make a list of the elements "San", "José", "State", "University"

using a list constructor? (Be sure to try your answer in the Scala interpreter)5. How do you make a list of the elements "San", "José", "State",

"University" using the cons (::) operator?

Step 2: List Functions

1. Write a recursive function concat that concatenates all strings in aList[String], yielding a String. Hint: (1) String concatenation is + in Scala,just like in Java (2) concat(Nil) is "". (3) Think about concat(lst) in terms oflst.head, lst.tail.

Give the code of your function.

2. What is the result of concat(List("San", "José", "State", "University"))?3. How can you modify concat so that it adds spaces between the strings (i.e.

so that concat(List("San", "José", "State", "University")) is "San JoséState University" but not "San José State University " or " San José StateUniversity"?

Step 3: More Recursion

1. Given a list, form a list of all pairs of elements (as lists of length 2) in some

order. If the original list has length n, form n2 pairs. What shouldpairs(List(1, 2, 3)) produce?

2. What is your strategy for recursion? Base case? Reduction to simpler case?3. What is your Scala code?

Step 4: Not Always Recursion

1. How can you implement the squares function from the lecture withoutrecursion?

2. The flatten method flattens out a list of lists into a list. To see what it does,just try out

List(List(1, 2),List(3, 4, 5)).flatten

Assuming that you are allowed to use this method, how can you implementthe pairs function of Step 3 without recursion?

CS 152 - Lecture 4

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Functions as Parameters

▪ Consider the map function:

val triple = (x : Int) => 3 * x(1 to 10).map(triple)// yields 3 6 9 12 ... 30

▪ Let's implement such a function. For simplicity, we only use setsand functions of Int

▪ Two parameters: List[Int], function (Int) => Int, returnsList[Int]

def map(lst : List[Int], fun: (Int) => Int) : List[Int] =

▪ Map of empty list is Nil, otherwise apply fun to lst.head and userecursion:

if (lst.isEmpty)Nil elsefun(lst.head) :: map(lst.tail, fun)

▪ Sample call:

map(List(1, 2, 3), (x : Int) => 3 * x)

▪ A function describes a piece of behavior, such as

What should map do with each element in lst?

Capturing the Enclosing Environment

▪ Consider this function:

val n = 3val fun = (x : Int) => n * x// What is fun(2)?

▪ n is not defined in the scope of fun, but that is ok. In the body of afunction, you can use any variable from the enclosing scope.

▪ Doesn't seem a big deal—n is immutable, so it will always be 3.But consider this:

def multiplyBy(n : Int) = (x : Int) => n * x

▪ Huh? Let's call it:

val quadruple = multiplyBy(4) // the function (x : Int) => 4 * x

▪ And let's call that:

quadruple(5) // yields 20

▪ Each call to multiplyBy yields a different function.▪ Each function has a different value of n▪ Closure = function + binding of its free variables (i.e the variables

that are not defined locally in the function)

Parameter Inference From Context

▪ When a function parameter type is known, you can supply ananonymous function without specifying its parameter types

def twice(f: (Int) => Int, x : Int) = f(f(x))twice((x) => 42 * x, 3)

// Ok, x : Int is inferred from context

▪ Very useful when calling library functions

List(1, 2, 3).map((x) => x * x)

• List[A].map(f : (A) => B) : List[B]• A is Int since List(1, 2, 3) is a List[Int]• f must be (Int)=> . . .• x must be Int

Parameter Simplifications

▪ Ok to omit () around a single inferred parameter

List(1, 2, 3).map(x => x * 0.5)List(1, 2, 3).sortWith((x, y) => x > y)

// need () with 2 or more parameters, or with 0 parameters

▪ Use _ for a parameter that only occurs once in the body

List(1, 2, 3).map(_ * 0.5)List(1, 2, 3).sortWith(_ > _)

▪ The _ can't be in an expression that is passed to another function.

List(1, 2, 3).map(math.sqrt(_ + 1)) // Error—Can't pass _ + 1 to math.sqrtList(1, 2, 3).map(math.sqrt(_) + 1) // Ok

Reductions

▪ map produces a list of values.▪ What if we want a single value?▪ In the previous lecture, we wrote

def sum(List[Int] lst): Int

▪ What about product? Max? Min?▪ The combining function should be an argument:

def reduce(lst: List[Int], op: (Int, Int) => Int): Int

▪ Call as

val result = reduce(lst, (x, y) => x + y)

▪ Or with anonymous variables:

val result = reduce(lst, _ + _)

▪ Implementation:

def reduce(lst: List[Int], op: (Int, Int) => Int): Int =if (lst.tail.isEmpty) lst.head elseop(lst.head, reduce(lst.tail, op))

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab3/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: Filters

1. What does the following function do? If in doubt, call it with a few values.

val isEven = (x : Int) => x % 2 == 0

2. Type (1 to 10).filter(isEven). As always, don't type the period, but typeENTER. What do you get?

3. Describe what filter does.

Step 2: A Random Number List

1. The random number generator in Scala is similar to that in Java. Whatoutput do you get from

val gen = new scala.util.Randomgen.nextInt(10)gen.nextInt(10)

2. What is the significance of the 10?3. We want to define a function randList(len : Int, n : Int) : List[Int] that

makes a list of length len of random integers between 0 and n - 1. Forexample, randList(5, 10) might yield a list of numbers 5 1 2 0 9. DefinerandList as a recursive function. What is the code of your function?

Hint: If len is 0, the result is nil. Otherwise, it is gen.nextInt(n) ::something. What is your definition?

Note: You need not define gen. You already defined it in part 1. Just useit.

4. What do you get for randList(5, 1000)? For randList(1000, 5)?5. Why is randList a closure?

Step 3: Filtering Large Numbers

1. Write a function greaterThan100(lst : List[Int]) that returns only thoseintegers in lst that are greater than 100. Don't use recursion; simply callfilter with an appropriate function:

def greaterThan100(lst : List[Int]) = {val fun = ... // your worklst.filter(fun) // NOTE: The last expression in a { ... } is its value

}

What is your function's code?

2. What is the value of greaterThan100(randList(10, 200))? Why does that giveyou confidence that you implemented everything correctly?

3. Of course, having a hard-wired bound of 100 is intensely ugly. Let'sgeneralize:

def greaterThan(n : Int, lst : List[Int]) = {val fun = ... // your worklst.filter(fun)

}

For example, greaterThan(50, nums) yields all values of nums > 50.

What is the code of your greaterThan function?

4. What is the value of greaterThan(100, randList(10, 200))?5. Why is the fun inside greaterThan a closure?

Step 4: Reduce

1. What is reduce(List(1,2,3,4,5), (x, y) => x - y)?2. Scala has two forms of reduce called reduceLeft and reduceRight. Try

(1 to 5).reduceLeft(_ - _)(1 to 5).reduceRight(_ - _)

What do each of them do?3. Given a list of digits, pick one of the two forms of reduce to compute the

decimal value. For example, List(1, 7, 2, 9) should turn into 1729. Hint:(x, y) => 10 * x + y.

4. We have implemented one of the forms of reduce in the lecture. Which one?Implement the other. Here is an outline:

def otherReduce(lst: List[Int], op: (Int, Int) => Int) = {def otherReduceHelper(lst: List[Int], op: (Int, Int) => Int, partialResult: Int): Int =

if (lst.isEmpty) ... elseotherReduceHelper(..., op, op(..., ...))

otherReduceHelper(..., op, ...)}

CS 152 - Lecture 5

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Higher Order Functions

▪ A higher order function consumes or produces another function.▪ Example of consumption: map

(1 to 10).map(x => x * x)

▪ Why would you want to produce a function?▪ And how?

Producing Functions

▪ We can define a function

def triple(x: Int) = 3 * x

▪ Then we can use it:

(1 to 10).map(triple)

▪ Here is another:

def quadruple(x: Int) = 4 * x(1 to 10).map(quadruple)

▪ What about a generic “multiply by n” function so that I can write:

(1 to 10).map(multiplyBy(5))

Producing Functions

▪ multiplyBy(5) needs to produce a function...▪ of type Int => Int:

def multiplyBy(n: Int) : Int => Int = ...

▪ It needs to produce a function that consumes an integer:

... = (x: Int) => ...

▪ and the result is n * x.▪ Now all together:

def multiplyBy(n: Int) : Int => Int = (x: Int) => n * x

▪ The return type is inferred:

def multiplyBy(n: Int) = (x: Int) => n * x

Comparators

▪ Comparators used for sorting:

List(1, 2, 3).sortWith(_ > _)

▪ Want to turn a discriminator function into a comparator:

strings.sortWith(comparingBy(s => s.length())

▪ What does comparingBy produce?• A function (String, String) => Boolean

▪ What does comparingBy consume?• A function String => Integer

▪ def comparingBy(f: String => Integer) =(x: String, y: String) => ... // A Boolean

▪ The rest is simple:

... => f(x) < f(y)

Composing Comparators

▪ Preparation: Make comparingBy generic.

def comparingBy[T, R : Ordering](f: T => R) = { // Don't worry about the detailsimport scala.Ordered.orderingToOrdered(x: T, y: T) => f(x) < f(y)

}

▪ Now it works for a map Person => String:

case class Person(first: String, last: String)val people = List(Person("Fred", "Flintstone"), Person("Fred", "Brooks"), Person("Barney", "Rubble"))people.sortWith(comparingBy(_.first))

▪ Now we want to compare by first name, using the last name tobreak ties:

people.sortWith(breakingTies(comparingBy(_.first), comparingBy(_.last)))

Composing Comparators

▪ What does breakingTies produce?▪ A function (Person, Person) => Boolean▪ What does breakingTies consume?▪ Two functions (Person, Person) => Boolean

▪ def breakingTies(c1: (Person, Person) => Boolean, c2: (Person, Person) => Boolean) =(x: Person, y: Person) => ... // A Boolean

▪ => if (c1(x, y)) true elseif (c1(y, x)) false elsec2(x, y)

Currying

▪ Currying = Turning a function that takestwo arguments into a function thattakes one argument. That functionreturns a function that consumes thesecond argument. (Named after thelogician Haskell Brooks Curry)

▪ Huh?▪ Simple example:

def multiply(x : Int, y : Int) = x * ymultiply(3, 4) is 12

▪ Now let's do this a little different:

def multiply(x : Int)(y : Int) = x * ymultiply(3)(4) is 12

▪ What is multiply(3)?▪ A function that is capable of eating 4, thus yielding 12:

def multiply(x: Int) = (y: Int) => x * y // That's our multiplyBy

▪ Can make higher-order functions easier on the eye:

def comparingBy(f: String => Integer) = (x: String, y: String) => f(x) < f(y)def comparingBy(f: String => Integer)(x: String, y: String) = f(x) < f(y)

▪ Similar to the rewrite of a val to a def:

val triple = (x: Int) => 3 * xdef triple(x: Int) = 3 * x

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab4/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: Short Function Notation

Define a variable lst as

val lst = List("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf")

What is the shortest command you can use to

1. Yield all short strings (of length < 5) from lst2. Yield the words sorted by increasing length3. Find the longest string (using reduce)

Step 2: Currying

▪ Write a function makeMin that consumes a comparator (a function(String, String) => Boolean) and produces a function (a:String, b: String) => String that computes the smaller of thetwo inputs, as measured by the comparator.

▪ Did you use Currying? If so, pat yourself on the back. Now do itthe other way around. Or if you did it the other way around, do itwith currying.

▪ Make your function generic. Just replace String with T and stick a[T] right after the function name. Don't worry—it won't hurt a bit.

Step 3: More Currying

1. Here is a function of computing the “maximum” string in a list that works forany ordering.

def max(lst : List[String], less : (String, String) => Boolean) =lst.reduce((x, y) => if (less(x, y)) y else x)

Make a call to max that yields the longest string in a list lst. Use _ for thestring parameters in your less function.

2. Now make this generic. Don't worry—it won't hurt a bit:

def max[T](lst : List[T], less : (T, T) => Boolean) =lst.reduce((x, y) => if (less(x, y)) y else x)

What happens when you call max(lst, _ < _)?

3. Ok, that didn't work so well. Currying to the rescue. Curry the max[T]function, exactly like multiply above. What is the code for your revisedfunction? What happens when you call max(lst)(_ < _)?

(Why does this work? Now the Scala type inferencer knows that T mustbe String after processing max(lst).)

CS 152 - Lecture 6

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Scala Documentation

▪ Online at http://www.scala-lang.org/api/2.12.4▪ Bookmark it right now!▪ Use text field in upper right corner to filter classes

Categories of List Methods

▪ Basic methods length head tail isEmpty▪ Operators :: :+ ++ == != /: :\▪ Access by position (n) take drop dropRight slice indexOflastIndexOf

▪ Methods without parameters reverse sorted sum max min▪ Methods with unary predicates count exists dropWhile filterfind findIndexOf forall partition remove span takeWhile

▪ Methods with unary function map reverseMap flatMap foreach▪ Methods with binary predicate sort▪ Methods with binary function reduceLeft reduceRight foldLeftfoldRight

▪ Other intersection union zip zipAll zipWithIndex mkString

List Operators

▪ :: appends in front, :+ in back

3 :: List(1, 2) is List(3, 1, 2)List(1, 2) :+ 3 is List(1, 2, 3)

▪ ++ concatenate lists

List(1, 2) ++ List(3, 4) is List(1, 2, 3, 4) // same as :::

▪ == and != compare lists

List("Hello", "World") == List("Hel" + "lo", "Wor" + "ld")

▪ /: and :\ later

Operator Precedence and Associativity

▪ On this calculator, what do you getwhen you type 1 + 2 * 3?

▪ What do you get in C++?▪ Precedence: Which operator is

executed first?

(1 + 2) * 31 + (2 * 3)

▪ Associativity: If the same operatoroccurs twice, is the left or right one executed first? Does 1 - 2 -3 mean

(1 - 2) - 31 - (2 - 3)

▪ In C++, most operators are left-associative. Exception:Assignment

a = b = 3

Scala Operators

▪ Function names can be any sequence of opchars: charactersother than whitespace, A-Z0-9()[]{}`'".,;

▪ Operator precedence depends on first character

(all letters)|^&< >= !:+ -* / %(all other special characters)

▪ Operators ending in : are right associative; all others are leftassociative

a :: b :: Nil is a :: (b :: Nil)

Lists: Access by Position

▪ Not very efficient for linked lists▪ Index values are 0-based▪ () are used for indexed access—not []

List(17, 29)(1) is 29

▪ slice takes sublist

List(2, 3, 5, 7).slice(1, 3) is List(3, 5)

Arguments to slice(from, to):

• from is the first index to include• to is the first index ≥ from to exclude

▪ See lab for drop take

Methods with Function Parameters

▪ Workhorse functions of the library▪ You already saw map, filter, reduce▪ Others explored in lab▪ Instead of looping, build a function and pass it to one of these

methods

def randList(len : Int, n : Int) =(1 to len).map((x : Int) => gen.nextInt(n))

▪ Some methods return a pair

List(2,3,5,7).partition(isEven) is (List(2),List(3, 5, 7))

Tuples

▪ Heterogeneous sequence of elements

val myFirstTuple = (1, 3.14, "Fred")

▪ Tuple type (Int, Double, String)▪ Access components with methods _1 (!), _2, _3

val second = myFirstTuple._2

▪ Or with destructuring

val (first, second, _) = myFirstTuple

Maps

▪ If I were stranded on a desert islandand could only take one data structurewith me, it would be the hash table. —Peter van der Linden

▪ Construct a map

val scores = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)

▪ -> operator makes pairs. "Alice" -> 10 is the same as ("Alice",10)

▪ Access element

val alicesScore = scores("Alice")// They don't call it a map for nothing

val fredsScore = scores.getOrElse("Fred", 0)

▪ Update map (i.e. get new map with updates)

val newScores = scores + ("Bob" -> 10, "Fred" -> 7)val newerScores = newScores - "Alice" // Remove key and value

flatMap• Given an integer sequence s, it is easy to form all pairs (x, 0):

val s = (1 to 4).toLists.map(x => (x, 0))

// List((1, 0), (2, 0), (3, 0), (4, 0))

• What if we want to have all pairs (x, y) where x, y are elements of s?

• Try it by calling map twice:

s.map(y => s.map(x => (x, y)))// List(List((1,1), (2,1), (3,1), (4,1)),// List((1,2), (2,2), (3,2), (4,2)),// List((1,3), (2,3), (3,3), (4,3)),// List((1,4), (2,4), (3,4), (4,4)))

• Close, but not quite. To “flatten out” the result, use flatMap instead:

s.flatMap(y => s.map(x => (x, y)))// List((1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), ..., (4, 4))

Folding

▪ The sum of the elements in List(a, b, c) is

a + b + c = 0 + a + b + c = ((0 + a) + b) + c

▪ Use foldLeft or the /: operator

def sum(lst: List[Int]) = (0 /: lst) ((x, y) => x + y)

▪ The /: indicates the tree shape

a+b+c/ \

a+b c/ \

a b/ \

0 a

▪ The first argument of the folding function is the partial answer; thesecond is the new list value to be considered

▪ To recurse may be divine, but not to recurse is even better:

def fac(n : Int) = (1 /: (1 to n)) {_ * _}

▪ The foldRight operator works right-to-left: a + (b + (c + 0))

mkString▪ toString produces the familiar List(1, 2, 3)▪ What if we want 1 | 2 | 3?

lst.mkString("|")

One fewer separator than elements

▪ What if we want [1 | 2 | 3]?

lst.mkString("[", "|", "]")

▪ Nice attention to detail by the library designers

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab5/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: Warmup

1. What do take and drop do? Give a brief explanation and an example foreach.

2. What is the difference between take and dropRight?3. Look up the definition of span in Scaladoc. Make an example that

demonstrates how span works. What is your example, and what value does itproduce?

4. The span method returns a pair. Show how you can get at each of theelements in that pair.

Step 2: Folding

1. Use the /: folding operator to concatenate all strings in a List[String],separating them with spaces. For example, if you start with val lst =List("Hello", "Scala", "World"), you should produce an expressioninvolving lst and /: that yields "Hello Scala World". (Hint: It is very easy toget " Hello Scala World". The challenge is to get rid of the first space.

2. Folding is useful for much more than computing sums and products. Manyalgorithms that compute a value by making a loop through an array can beobtained with a suitable function whose first argument is the result from theelements that you have seen so far, and whose second argument is the nextlist element.

Consider the case of computing the maximum. Find a suitable functionwhose first argument is the maximum of all elements visited so far, andwhose second argument is the next element.

What the code for your function maximum(lst : List[Int]) : Int? (Youmay assume that the list has length > 0)

3. What does this function do?

def mystery(lst : List[Int]) =(List[Int]() /: lst) ((x, y) => if (y % 2 == 0) x else x :+ y)

Explain how the function works.

Step 3. A Puzzle

Here is a puzzle from a programmer web site:

Given an array of numbers, return the occurances [sic] of the number 1 in the array. So, if thearray contains 1, 2, 11, 13 you would return 4 (as 11 contains two instances of 1).

a. Suppose you are given a list of numbers, such as List(1, 2, 11, 13). To getdigits of one, it would be better to have strings. How do you get an array ofstrings? Hint: toString, map

b. Now look at an individual string such as "1811". How can one count thenumber of ones? Hint: (1) Strings are collections of characters (2) Try thecount method that takes a predicate. For example, (1 to 10).count(_ % 3 ==0) yields 3

c. Ok, now you need to apply that to each of the strings. How do you do that?(Hint: map)

d. That gives you a collection of counts. How do you total them up? Hint:reduce, or, if you prefer, look at the scaladoc of Seq for a simpler way.)

e. Now all together. If lst is a list of integers, how do you get the total numberof digits of one?

Step 4: Misery with Tuples

1. What is 1 -> 2 -> 3? What is the type of the result?2. Write a function flatten that takes such a thing and turns it into an (Int,

Int, Int). Use _1, _23. Repeat with destructuring.4. What is the result of the following statement, and why?

val x,y,z = (1,2,3)

Step 5: If I Had Just One Data Structure...

1. And if I could just take one method, it would be groupBy. Load all words froma dictionary into a list by calling

val words = io.Source.fromURL("http://horstmann.com/sjsu/spring2018/cs152/words").getLines.toList

What is words.groupBy(w => w.length)?2. Make a map so that myMap('a') yields all words that start with the letter a and

so on.3. How many words start with a given letter? Hint: myMap.map(...)4. Extra credit: Turn this into a list of tuples that is sorted by frequency. Hint:

sorted doesn't work because Scala doesn't know how to sort the tuples.There are two other sort methods. One of them wants a function that mapsinto something that Scala does know how to sort—the easy choice forsolving this exercise.

5. Extra credit: Now do it with the other method (and some tuple misery).

CS 152 - Lecture 7

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Language Translation Process

▪ Input: Source code▪ Lexical Analysis: Group input characters into reserved words,

identifiers, constants, operators, comments▪ Parsing: Build data representation of program, report syntax

errors▪ Code generation: Use data representation to generate, optimize

code▪ Output: Machine or VM code▪ Interpreter directly executes the parsed program

Lexical Analysis

▪ Remove white space▪ Remove comments▪ Break input into tokens▪ if ( radix >= 0.0) y = sqrt(radix)▪ Each token has type (reserved word, identifier, constant,

operator, etc.) and text▪ Use regular expressions to define token types (see below)▪ Process header files (C/C++)

Notes on White Space

▪ Free form language: All white space is optional▪ Line-oriented language: Line endings are significant. Ex.: Fortran▪ Scala is in the middle: Newline is a token and can be equivalent

to a ;▪ Python: Indentation indicates block structure

if radix >= 0:y = sqrt(radix)print(y)

else:print('error')

Parsing

▪ Translates stream of tokens into data structure▪ Parse tree for expression 3 + 4 * x

▪ Higher-level features have more complex structure

Ex. function has

• name• return type• list of parameters• body

Body has

• list of expressions (Scala)/statements (Java)

Code Generation

▪ Parse tree translated into machine or VM instructions▪ Example:

movl $4, %eaxmull x, %eaxaddl $3, %eax

Code Optimization

▪ Register allocation

result = (a + b) * (c + d)

movl a, %eaxaddl b, %eax

movl c, %ebxaddl b, %ebx

mull %ebx, %eaxmovl %eax, result

▪ Loop optimization

for (i = 0; i < n; i++) {t = u * v;a[i] = t + i;

}

▪ Inline functions

int max(int x, int y) { return x > y ? x : y; }

Translate max(x, 0) into x > 0 ? x : 0, avoid cost of functioncall

▪ Many others

Regular Expressions

▪ Describe “regular” sets of strings▪ Symbols other than ( ) | * stand for themselves▪ Concatenation α β: First part matches α, second part β▪ α | β = Match α or β▪ α* = 0 or more matches of α▪ Use () for grouping▪ Example: E(0|1|2|3|4|5|6|7|8|9)*

An E followed by a (possibly empty) sequence of digits

E123E9E

Regular Expressions

Convenience Notation

▪ α+ = one or more (i.e. αα*)▪ α? = 0 or 1 (i.e. (|α))▪ [xyz] = x|y|z▪ [x-y] = all characters from x to y, e.g. [0-9] = all ASCII digits▪ [^x-y] = all characters other than [x-y]▪ \p{Name}, where Name is a Unicode category (ex. L, N, Z for

letter, number, space)▪ \P{Name}: complement of \p{Name}▪ . matches any character▪ \ is an escape. For example, \. is a period, \\ a backslash

Regex Examples

▪ Integers: [+-]?[0-9]+, or maybe [+-]?\p{N}+• Note: + loses its normal meaning inside [], and a - just before ] denotes itself

▪ Hexadecimal numbers 0[Xx][0-9A-Fa-f]+▪ Quoted Java strings: ".*"▪ Well, actually not; the . will match a quote. Better: "[^"]*"▪ Well, actually not; you can have a \" in a quoted string."([^"\\]|\\.)*"

▪ Are we done yet? What about \x1B?

The Scala Regex Library

▪ Find all matches

import scala.util.matching._val regex = "[0-9]+".rregex.findAllIn("99 bottles, 98 bottles").toListList[String] = List(99, 98)

Check whether beginning matches

regex.findPrefixOf("99 bottles, 98 bottles").getOrElse(null)String = 99

▪ Groups

val regex = "([0-9]+) bottles".rval matches = regex.findAllIn("99 bottles, 98 bottles, 97 cans").matchData.toListmatches : List[Regex.Match] = List(99 bottles, 98 bottles)

matches.map(_.group(1))List[String] = List(99, 98)

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab6/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: Regular Expressions

Write down regular expressions for

▪ Java floating-point literals▪ Java character constants▪ C-style comments, i.e. /* ... */

Step 2: Check Your Regexes

Use the Scala Regex.findAllIn method to check your work from Step 1 with thestrings

▪ "3.14 -3.14 +3.14 3. 3 .3 3.14E2 3.14E+2 3.14E-2 3.14e100"

▪ "'a' '\\n' '\\\\' '\\'' '\\x1b' '\\033'"

▪ "/* foo */ /** foo */ /**/**/**/ /*/*/*/"

What results do you get?

Tip: In Java, it is unpleasant to deal with strings containing backslashes andquotes. For example, the regular expression ([^"\\]|\\.)*, as a Java string, is"([^\"\\\\]|\\\\.)*" Scala has an alternate way of specifying strings. Whenstrings are enclosed in """...""", nothing inside is escaped. (Of course, you can'thave a """ inside.) For example, """([^"\\]|\\.)*""".

Step 3: A Simple Lexer

In a lexer, we specify a set of patterns that are tried in sequence. For example, asimple language might have the following token types:

▪ Reserved words: if|def|val▪ Identifiers: \p{L}(\p{L}|\p{N}|_)*▪ Literals: [+-]?\p{N}+▪ Operators: [+*/%<=>-]▪ Syntactical marks: [(){};]▪ White space: \p{Z}+

Note that the order matters. We want if, def, val recognized as reserved words,not identifiers.

In this step, write a function firstMatch(input : String, patterns : List[Regex]):String that returns the first match in the input string for any of the regularexpressions or null if there is no match. For example,

val patterns = List("if|def|val".r, """\p{L}(\p{L}|\p{N}|_)*""".r,"""[+-]?\p{N}+""".r, "[+*/%<=>-]".r, "[(){};]".r, """\p{Z}+""".r

)val input = "if(x<0) 0 else root(x);"firstMatch(input, patterns)String : iffirstMatch(input.substring(2), patterns)String : (

What is the code for your function?

Hint: This is simple recursion. If the first regex matches, return the match,otherwise call firstMatch(input, patterns.tail).

Step 4: Complete the Lexer

Write a function tokens(input : String, patterns : List[Regex]) : List[String]that returns a list of matching tokens. For example,

tokens(input, patterns)List[String] = List(if, (, x, <, 0, ), , 0, , else, , root, (, x, ), ;, )

That's again simple recursion. If the input is empty, return the empty list.Otherwise, get the first match. If it's null, return the empty list. Otherwise,recursively call tokens(input.substring(first.length), patterns).

What is the code of your function?

CS 152 - Lecture 8

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Scala Classes

▪ A simple class:

class Point(val x: Double, val y: Double) {def move(dx: Double, dy: Double) = new Point(x + dx, y + dy)def distanceFromOrigin = math.sqrt(x * x + y * y)override def toString = "(" + x + ", " + y + ")"

}

▪ Constructor with two arguments.

val myFirstPoint = new Point(3, 4)

▪ Five methods: x, y, move, distanceFromOrigin, toString▪ Need override when overriding methods (here Object.toString)▪ No () for parameterless accessor methods

Construction Parameters

▪ val parameter gives rise to instance variable and accessor

Point(val x, val y)

▪ Parameter without val gives rise to instance variable withoutaccessor.

class Rand(gen: java.util.Random) { // No valdef value = gen.nextInt()

}

▪ Can also have private instance variable

class Rand {private val gen = new java.util.Random(42)def value = gen.nextInt()

}

▪ Calling superclass constructor:

class LabeledPoint(x: Double, y: Double, val label: String)extends Point(x, y)

Scala Objects

▪ Scala object = class with only one instance▪ Extend the App trait (similar to an interface) for an application

object:

object Main extends App {println("My first Scala App")

}

▪ A class can have a companion object with methods that aresimilar to static methods in Java. Examples:

List.tabulateSource.fromURL

▪ In Scaladoc, click on the circle with C or T to switch to thecompanion object, denoted with O

▪ math.ceil is not defined in an object—it's in the scala.mathpackage

Case Classes

▪ Special kind of class, optimized for matching

case class ClassName(field1 : Type1, field2 : Type2, ...) extends Superclass

▪ Example: Binary tree with values only in the leaves

abstract class SimpleTreecase class Leaf(value : Int) extends SimpleTreecase class Node(left : SimpleTree, right : SimpleTree) extends SimpleTree

▪ Construct instances with ClassName(arg1, arg2, ...)

Node(Node(Leaf(3), Leaf(2)), Node(Leaf(7), Node(Leaf(6), Leaf(8))))

Pattern Matching

▪ selectorExpr match {case pattern => expr...case pattern => expr

}

▪ Many possibilities for the patterns; we care about case classes

▪ tree match {case Node(l, r) => expr1case Leaf(v) => expr2

}

▪ Variables l, r, v are bound to the values in the case classinstances; you can use them in the expressions.

def sum(t : SimpleTree) : Int = t match {case Node(l, r) => sum(l) + sum(r)

case Leaf(v) => v}

▪ Matches are tried in order. To have a default, use the “wildcard”pattern at the end

case _

Syntax Trees

▪ Tree that represents the syntax of a program (or a program part)▪ Example: Expression tree for 3 + 4 * x

▪ Model in Scala:

class Exprcase class Number(value : Int) extends Exprcase class Variable(name : String) extends Exprcase class Operator(left : Expr, right : Expr,

f: (Int, Int) => Int) extends Expr

Operator(Number(3), Operator(Number(4), Variable("x"), _ * _), _ + _)

▪ Next lecture: How to parse 3 + 4 * x into Scala value

Syntax Tree Evaluation

▪ Compute value of expression▪ Need values of free variables▪ Use a Map[String, Int]▪ Scala immutable map refresher:

• Map(key1 -> value1, key2 -> value2, ...) yields map with given key/valuepairs

• map + (key -> value) yields map with new key/value pair• map(key) yields value of key (must exist)• map.get(key) yields Option, either Some(value)or None

▪ def eval(expr : Expr, symbols : Map[String, Int]) : Int =expr match {

case Number(num) => numcase Variable(name) => symbols(name)case Operator(left, right, f) => f(eval(left, symbols), eval(right, symbols))

}

Functions with Variable Arguments

▪ Want to call a function as:

val result = sum(1, 7, 2, 9)val result2 = sum(3, 1, 4, 1, 5, 9, 2, 6)

▪ Define the argument type as Int*

def sum(args: Int*) = args.reduce(_ + _)

▪ Argument is wrapped into a Seq[Int]▪ If you already have a Seq[Int], you cannot pass it directly.▪ Use : _* ascription:

val s = sum(1 to 5: _*) // Consider 1 to 5 as an argument sequence

▪ Necessary in recursive definitions:

def recursiveSum(args: Int*) : Int = {if (args.length == 0) 0else args.head + recursiveSum(args.tail : _*)

}

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab7/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1

Add the classes Expr, Number, Variable, Operator to a Main.scala file, inside a Mainobject. Add the eval method to the Main object. In the body of the Main object,construct the tree

print it and and evaluate it with a symbol table in which x is 5. What is the code ofyour Main.scala file?

Step 2

Now we want to process variable definitions (which we will write as val x = expr inthe next lecture). For now, we will assume that they are already parsed intoinstances of

case class Definition(name : String, expr : Expr)

For example, a definition val x = 2 would be a Definition("x", Number(2)). Adefinition changes the symbol table. In a functional setting, that means we need toreturn a new table that contains all bindings in the old table and the new binding.For example,

val def1 = Definition("x", Number(2))val def2 = Definition("y", Variable("x"))val sym0 = Map[String, Int]()val sym1 = eval(def1, sym0) // "x" -> 2val sym2 = eval(def2, sym1) // "x" -> 2, "y" -> 2System.out.println(sym2)

Implement this eval method. What is the code of your method? (You can get thefields of a definition as defi.name, defi.expr.)

Note: This eval method requires you to call the eval method that we defined forexpressions, but it is a different method—it consumes a definition and a symboltable, yielding another symbol table.

CS 152 - Lecture 9

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Context-Free Grammar

▪ Language: Set of valid token sequences▪ Terminal symbol: Token in the language. Ex: " class" "3.14" "+"▪ Nonterminal symbol: Symbols in the grammar. Ex. expression▪ Production: Rule for replacing a nonterminal by a sequence of

nonterminals and terminals

term ::= factor "*" term

▪ Can have more than one production for the same nonterminal

term ::= factor

▪ Derivation: Sequence of productions

term ::= factor "*" term::= factor "*" factor "*" term::= factor "*" factor "*" factor

▪ Context-free: The replacement doesn't depend on the context ofthe nonterminal

Parse Tree

▪ Shows derivation process

▪ Not the same as an expression tree!▪ Parser: Component that follows derivation process, yielding some

actions▪ Most common actions: Building an expression tree. (Next lecture)

Grammar Example: List of Numbers

▪ Want to generate

(1)(1, 24, 4)()

▪ list ::= "(" contents ")"

▪ contents ::=contents ::= numbercontents ::= number "," contents

▪ No, that's wrong. We could derive (1, 24, )

Grammar Example: List of Numbers

▪ Try again

▪ list ::= "(" contents ")"list ::= "(" ")"

▪ contents ::= numbercontents ::= number "," contents

▪ Easy to see by induction that contents yields n numbersseparated by n - 1 commas

Extended BNF

▪ Backus-Naur form: Original syntax for grammar rules in Algol-60▪ Extended by conveniences for frequently recurring constructs▪ Alternatives A | B▪ Repetitions

• 0 or more (A)* or {A}• 1 or more (A)+• 0 or 1 (A)? or [A]

▪ For example,

list ::= "(" ( number ( "," number )* )? ")"

Parser Generators

▪ Parser generator: Produces a parser from a given grammar▪ Works in conjunction with lexical analyzer▪ Examples: yacc, Antlr, JavaCC▪ Programmer specifies actions to be taken during parsing▪ Far better than writing a parser by hand▪ No free lunch: need to know parsing theory to build efficient

parsers▪ Scala has a built-in “combinator parser”

Combinator Parser

▪ Each nonterminal becomes a function▪ Terminals (strings) and nonterminals (functions) are combined

with operators• Sequence ~• Alternative |• 0 or more rep(...)• 0 or 1 opt(...)

class SimpleLanguageParser extends JavaTokenParsers {def expr: Parser[Any] = term ~ opt(("+" | "-") ~ expr)def term: Parser[Any] = factor ~ opt(("*" | "/" ) ~ term)def factor: Parser[Any] = wholeNumber | "(" ~ expr ~ ")"

}

▪ Will replace Parser[Any] with something more useful later

Combinator Parser Result

▪ String returns itself▪ opt(P) returns Option: Some of the result of P, or None (see next

slide)▪ rep(P) returns List of the results of P▪ P ~ Q returns instance of class ~ (similar to a pair)▪ For example,

val parser = new SimpleLanguageParserval result = parser.parse(parser.expr, "3 - 4 * 5")

sets result to

((3~None)~Some((-~((4~Some((*~(5~None))))~None))))

▪ We'll transform this to something more useful in the next lecture

▪ After changing (x~None) to x, Some(y) to y, and ~ to spaces:

(3 (- (4 (* 5))))

The Option Type

▪ Option type expresses “0 or 1” relationship▪ Case class with two cases: Some, None▪ Example: List.find returns Option[A].

val result = lst.find(_ % 2 == 0)result match {

case Some(x) => println(x)case None => println("No match")

}

▪ Why not return null if there is no match?▪ Int can't be null▪ null might be a valid value in the collection▪ null can lead to NullPointerException

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab8/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 0

1. Make an SBT project lab8:

cd cs152 # (or whereever you store your git repo)PROJECT=lab8echo $PROJECT | sbt new sbt/scala-seed.g8echo $PROJECT | sbt new cayhorstmann/cs152-seed.g8cd $PROJECTfind . -name Hello*.scala -exec rm {} \;sed -i -e "s/Hello/$PROJECT/" build.sbt

2. Edit build.sbt and change

libraryDependencies += scalaTest % Test

to

libraryDependencies ++= Seq(scalaTest % Test,"org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.0")

You can do this with a text editor or boldly with

sed -i -e 's/= scalaTest % Test/+= Seq\(scalaTest % Test, "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.0"\)/' build.sbt

(Thanks to Richard Faustino for this tip.)

3. If you use Eclipse, run

echo 'addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")' > project/plugins.sbtsbt eclipse

Then start Eclipse and import the project.4. If you use IntelliJ, load the SBT project from the lab8 directory.

Step 1

1. Here is the complete application that was discussed in the lecture:

import scala.util.parsing.combinator._

class SimpleLanguageParser extends JavaTokenParsers {def expr: Parser[Any] = term ~ opt(("+" | "-") ~ expr)def term: Parser[Any] = factor ~ opt(("*" | "/" ) ~ term)def factor: Parser[Any] = wholeNumber | "(" ~ expr ~ ")"

}

object Lab8 extends App {val parser = new SimpleLanguageParserval result = parser.parse(parser.expr, "3 - 4 * 5")println(result.get)

}

2. Run the application. What is the output?3. Now change the expression to 3 * 4 - 5. How does the output differ?4. How does this indicate that the grammer “knows” that multiplication binds

more strongly than addition?

5. The factor rule recurses back to the expr rule. Give an example input to theparser that demonstrates this feature. What is your input? What is theoutput?

6. What happens when you give an illegal input to the parser? What input didyou give, and what was the result?

Step 2

1. Add the following function to the SimpleLanguageParser (and not to Lab8):

def eval(x : Any) : Int = x match {case a ~ Some("+" ~ b) => eval(a) + eval(b)case a ~ Some("-" ~ b) => eval(a) - eval(b)case a ~ Some("*" ~ b) => eval(a) * eval(b)case a ~ Some("/" ~ b) => eval(a) / eval(b)case a ~ None => eval(a)case a : String => Integer.parseInt(a)case "(" ~ a ~ ")" => eval(a)

}

2. In your main program, print the value computed byparser.eval(result.get). What output do you get when the parser inputis 3 - 4 * 5? 3 * 4 - 5?

3. What does the eval function do?4. Give an input that shows that the last matching rule is necessary. What is

your input? What is the output? What result do you get when you commentout the last matching rule?

Step 3

1. Now we want to enhance the language and add support for statements suchas

val x = 3 + 4 * y

In BNF, this would be

valdef ::= "val" identifier "=" expr

Add a valdef type to the combinator parser that represents such adefinition. (To see how to parse an identifier, go to the scaladoc ofJavaTokenParsers and then click on the source link on the top of the file.)

What is the type that you added?

2. What do you get when you parse val x = 3 + 4? Hint: Remember to call

parser.parse(parser.valdef, "val x = 3 + 4")

3. What happens when you parse val x = 3 + 4 * y? (Hint: Look closely at theoutput. Where is the y? Why?)

4. What do you do to fix the problem? (Hint: Handle variable identifiers at thesame level as numbers.)

5. Now what is the parser output?

CS 152 - Lecture 10

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Reminder: Context-Free Grammar

▪ Productions

expr ::= term (( "+" | "-" ) expr)?term ::= factor (( "*" | "/") term)?factor ::= wholeNumber | "(" expr ")"

▪ Non-terminals expr term factor▪ Terminals (tokens) wholeNumber " (" "+" ...▪ Parse tree shows derivation process

Not the same as an expression tree!

Reminder: Scala Combinator Parser

▪ Each nonterminal becomes a function▪ Terminals (strings) and nonterminals (functions) are combined

with operators• Sequence ~, returns instance of class ~ (similar to a pair)• Alternative |• 0 or more rep(P), returns List of the results of P• 0 or 1 opt(P), returns Option: Some of the result of P, or None

class SimpleLanguageParser extends JavaTokenParsers {def expr: Parser[Any] = term ~ opt(("+" | "-") ~ expr)def term: Parser[Any] = factor ~ opt(("*" | "/" ) ~ term)def factor: Parser[Any] = wholeNumber | "(" ~ expr ~ ")"

}

Transforming Combinator Parser Results

▪ Default combinator parser result is inconvenient

((3~None)~Some((-~((4~Some((*~(5~None))))~None))))

▪ Use ^^ operator to transform. For example,

wholeNumber ^^ (_.toInt)

▪ Use pattern matching for transforms

def expr: Parser[Int] = (term ~ opt(("+" | "-") ~ expr)) ^^ {case a ~ None => acase a ~ Some("+" ~ b) => a + bcase a ~ Some("-" ~ b) => a - b

}

▪ Use ~>, <~ to discard tokens

def factor: Parser[Int] = wholeNumber ^^ (_.toInt) |"(" ~> expr <~ ")"

▪ The Parser[...] type must match the return type of thetransforms (here, Int)

Returning Expression Trees

▪ Returning Int works if we interpret an arithmetic expressionwithout variables

▪ If we have variables in a loop, need to evaluate multiple times▪ Want an expression tree: Parser[Expr]

class Exprcase class Number(value : Int) extends Exprcase class Variable(name : String) extends Exprcase class Operator(left : Expr, right : Expr,

f: (Int, Int) => Int) extends Expr

▪ class SimpleLanguageParser extends JavaTokenParsers {def expr: Parser[Expr] = (term ~ opt(("+" | "-") ~ expr)) ^^ {

case a ~ None => acase a ~ Some("+" ~ b) => Operator(a, b, _ + _)case a ~ Some("-" ~ b) => Operator(a, b, _ - _)

}

...}

▪ Returns tree such as

Operator(Number(3),Operator(Number(4),Number(5),<function>),<function>)

A Grammar Problem

▪ Consider the input 3 - 4 - 5▪ Parse:

expr ::= term - expr::= term - term - expr

▪ Resulting expression tree:

▪ That's the wrong tree. 3 - (4 - 5) is 3 - (-1) = 4▪ We want - to be left associative: (3 - 4) - 5▪ How about switching expr and term?▪ In theory, this works, but in practice, the Scala combinator parser

gets into an infinite recursion

expr ::= expr - term::= expr - expr - term:: expr - expr - expr - term

Remedy: Manually Group Terms

▪ Reorganize Grammar

class SimpleLanguageParser extends JavaTokenParsers {def expr: Parser[Expr] = term ~ rep(("+" | "-") ~ term)def term: Parser[Expr] = factor ~ rep(("*" | "/" ) ~ factor)def factor: Parser[Expr] = wholeNumber | "(" ~ expr ~ ")"

▪ Now we have a list of all terms▪ Manually group them left to right

Operator(Operator(...(Operator(term1, term2, op1), term3, op2), ...)

▪ Reminder: foldLeft or the /: operator

def sum(lst: List[Int]) = (0 /: lst) ((x, y) => x + y)

▪ The /: indicates the tree shape

a+b+c/ \

a+b c/ \

a b/ \

0 a

Use foldLeft

▪ Parsing 3 - 4 - 5 yields

3 ~ List("-" ~ 4, "-" ~ 5)

▪ Note that the tail is a flat list▪ Want the tree

-6/ \

-1 "-" ~ 5/ \

3 "-" ~4

▪ Initial element is 3▪ Next element is "-" ~ 4▪ Fold operator is

case (x, "+" ~ y) => Operator(x, y, _ + _)case (x, "-" ~ y) => Operator(x, y, _ - _)

▪ Now everything together:

def expr: Parser[Expr] = (term ~ rep(("+" | "-") ~ term)) ^^ {case a ~ lst => (a /: lst) {

case (x, "+" ~ y) => Operator(x, y, _ + _)case (x, "-" ~ y) => Operator(x, y, _ - _)

}

▪ Result: Parser yields Expr with the correct structure▪ Next lecture: What can we do with the tree?

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab9/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1

Complete the program from slides 3 and 4.

import java.io._import scala.util.parsing.combinator._

class SimpleLanguageParser1 extends JavaTokenParsers {def expr: Parser[Int] = (term ~ opt(("+" | "-") ~ expr)) ^^ {

case a ~ None => acase a ~ Some("+" ~ b) => a + bcase a ~ Some("-" ~ b) => a - b}

. . .def factor: Parser[Int] = wholeNumber ^^ (_.toInt) | "(" ~> expr <~ ")"

}

object Main extends App {val parser = new SimpleLanguageParser1val result = parser.parse(parser.expr, new InputStreamReader(System.in))println(result)

}

1. How did you complete the parser?2. What result do you get when you input 3 + 4 * 5?

3. On your platform, how did you have to indicate the end of console input? (Ihad to type Ctrl+D in Linux.)

4. Why isn't the output a tree?

Step 2

Complete the program from Slide 5.

import java.io._import scala.util.parsing.combinator._

class Exprcase class Number(value : Int) extends Exprcase class Variable(name : String) extends Exprcase class Operator(left : Expr, right : Expr,

f: (Int, Int) => Int) extends Expr

class SimpleLanguageParser2 extends JavaTokenParsers {def expr: Parser[Expr] = (term ~ opt(("+" | "-") ~ expr)) ^^ {

case a ~ None => acase a ~ Some("+" ~ b) => Operator(a, b, _ + _)case a ~ Some("-" ~ b) => Operator(a, b, _ - _)}

...}

object Main {def main(args : Array[String]) : Unit = {}val parser = new SimpleLanguageParserval result = parser.parse(parser.expr, new InputStreamReader(System.in))

println(result)}

NOTE: Unfortunately, (Number(_.toInt)) doesn't work. Use (x =>Number(x.toInt))

1. How did you complete the code?2. What did you get when you parsed 3 + 4 * 5?3. What did you get when you parsed 3 - 4 - 5?4. What happens when you flip expr and term in the right hand side of the first

production? Try parsing 3 - 4 - 5 again.

Step 3

Complete the program from slide 8:

import java.io._import scala.util.parsing.combinator._

class Expr...

class SimpleLanguageParser3 extends JavaTokenParsers {def expr: Parser[Expr] = (term ~ rep(("+" | "-") ~ term)) ^^ {

case a ~ lst => (a /: lst) {case (x, "+" ~ y) => Operator(x, y, _ + _)case (x, "-" ~ y) => Operator(x, y, _ - _)

}}

...}

object Main extends App {...

}

1. How did you complete the program?

2. What did you get when you parsed 3 - 4 - 5?3. We never did anything about Variable. How can you enhance your program

to parse them as well, e.g. 3 - 4 * x?

CS 152 - Lecture 11

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Parsing 5

▪ Grammar techniques▪ Repetition▪ Precedence and associativity▪ LL(1) grammars▪ Avoiding left recursion▪ Left factoring▪ Ambiguity

Reminder: Context-Free Grammar

▪ Productions

expr ::= term (( "+" | "-" ) expr)?term ::= factor (( "*" | "/" ) term)?factor ::= wholeNumber | "(" expr ")"

▪ Non-terminals expr term factor▪ Terminals (tokens) wholeNumber "(" "+" ...▪ Parse tree shows derivation process

Repetition

▪ Simple repetition, ex. array bounds [10][10][20]

bounds ::= bound | bound boundsbound = "[" number "]"

▪ Or in EBNF

bounds ::= (bound)+

▪ In Scala, simply collect all

def bounds = bound ~ rep(bound) ^^ { case head ~ tail => head :: tail }bound = "[" ~> number <~ "]" ^^ { _.toInt }

▪ Or use rep1 convenience combinator

def bounds = rep1(bound)

No transform needed

▪ Repetition with separators, ex. function arguments id(arg1,arg2, ...)

funcall ::= ident ~ "(" ~ expr ("," expr)* ")"

▪ Scala convenience combinator

def funcall = ident ~ "(" ~> repsep(expr, ",") <~ ")"

repsep returns List without separators; here, List[Expr]

Operator Precedence

▪ Term/factor levels make * / stronger than + -:

expr ::= term (( "+" | "-" ) expr)?term ::= factor (( "*" | "/" ) term)?factor ::= wholeNumber | "(" expr ")"

▪ Even stronger operator, say ^ for “raise to a power”

term ::= factor (( "*" | "/" ) term)?factor ::= factor1 ( "^" factor )?factor1 ::= wholeNumber | "(" expr ")"

▪ Weaker operator, say == <>

expr ::= expr1 (( "==" | "<>" ) expr)?expr1 ::= term (( "+" | "-" ) expr1)?term ::= factor (( "*" | "/" ) term)?factor ::= wholeNumber | "(" expr ")"

Operator Associativity

▪ Right recursion is naturally right associative

assignment ::= ident ":=" assignment | ident ":=" expr

▪ For example,

a := b := 3

parses as

▪ Left recursion is naturally left recursive, but undesirable for us▪ See Lecture 10 for remedy

LL(1) Grammars

▪ Start from the grammar root▪ Apply productions until input string is reached▪ Which production?▪ With some grammars, choice can be deterministic▪ Desirable to have one-token lookahead property, called LL(1):

Only one production can be chosen depending on the next token▪ Example: 3 + 4 * 5▪ Start from grammar root: expr ::= term (( "+" | "-" ) expr)?▪ After matching 3 as term → factor → wholeNumber, back in term:

term ::= factorterm ::= factor ("*" | "/") term

▪ Should we go on with ("*" | "/") term? No—next token is "+"

Avoiding Left Recursion

▪ A grammar rule is left recursive if the left hand side appears atthe beginning of the right hand side

expr ::= expr "+" term | term

▪ Not LL(1) because there is no way of choosing between the tworules

▪ Infinite recursion in Scala combinator parser▪ Can be removed by observing that eventually expr must be a term

expr ::= term | term restrest ::= "+" term rest

▪ Annoyance: Right recursion not convenient for left-associativeoperators

▪ Scala solution: Collect all terms and fold

def expr = term ~ rep("+" ~> term) ^^{ case a ~ lst => (a /: lst) { Sum(_, _) } }

(See previous lecture for details)

▪ Left recursion is not a problem for bottom-up parsers (which wedon't discuss in this class)

Left Factoring

▪ If a grammar has rules of the form

A ::= α β | α γ

it can't be LL(1)

▪ Example:

Assignment statement and procedure call both start withidentifier

stat ::= ident ":=" expr | ident "(" expr ("," expr)* ")" | ...

▪ Factor out common left

stat ::= ident rest | ...rest ::= ":=" expr | "(" expr ("," expr)* ")"

▪ Annoyance: Complicates semantic analysis because identifier isseparated from assignment or call

▪ Scala solution: opt and match

def stat = ident ~ (":=" expr | "(" expr ")")

Ambiguity

▪ if with optional else

ifstat ::= "if" "(" expr ")" stat ("else" stat)?

▪ Gives rise to ambiguous construct

if (c1) if (c2) s1 else s2

or

▪ Disambiguation depends on parser technology▪ Scala: The obvious rule gives the expected behavior (see Lab)

(("if" ~ "(") ~> expr <~ ")") ~ stat ~ opt("else" ~> expr)

Reading Grammars

▪ Useful skill, even outside programming language theory▪ E.g. to determine correct file formats▪ Variation in grammar syntax▪ Terminals vs. nonterminals—some authors enclose nonterminals

in <...>

<expr> ::= <term> | <term> (+ | -) <expr>

Then no "..." around terminals

▪ [...], {...} instead of (...)?, (...)*

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab10/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1

1. Look at the Annotated XML Specification Section 3.1. Can you have spacesbefore an element name in a start tag?

< name>

Which grammar rule did you use to answer this question?

2. Before the /> in an empty-element tag?

<name />

Which grammar rule did you use to answer this question?

3. Around the = in an attribute?

<name attr = "value">

Which grammar rule did you use to answer this question?

Step 2

1. Write a Scala parser for the grammar

expr ::= "if" "(" number ")" expr ("else" expr)? | number

What is your program? What do you get when you parse

if (1) if (2) 3 else 4

2. Ok, it's probably too tedious to figure out whether the else associates withthe first or second if. Enhance your program to yield a IfExpr. Use thefollowing outline:

class Exprcase class IfExpr(cond : Number, pos : Expr, neg : Expr) extends Exprcase class Number(value : String) extends Expr

class SimpleLanguageParser extends JavaTokenParsers {def expr: Parser[Expr] = ...def number: Parser[Number] = wholeNumber ^^ { Number(_) }

}

Transform cond ~ expr ~ None into IfExpr(cond, expr, null).

Now what do you get for

if (1) if (2) 3 else 4

3. Does the Scala parser resolve the if/if/else ambiguity in the same way asC++, or the other way?

CS 152 - Lecture 12

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

The Simple Language SL1

▪ Grammar

block ::= (valdef | fundef)* exprexpr ::= expr2 | "if" "(" expr ")" block "else" blockexpr2 ::= term (( "+" | "-" ) term)*term ::= factor (( "*" | "/" ) factor)*factor ::= wholeNumber | "(" expr ")" | ident | funcall | funliteralfuncall ::= ident "(" (expr ("," expr)*)? ")"funliteral ::= "{" (ident ("," ident)*)? "=>" block "}"valdef ::= "val" ident "=" expr ";"fundef ::= "def" ident "=" funliteral ";"

▪ Similar to Scala subset, but• Only two types: integer and functions• No compile-time type checking• if fakes Boolean similar to C: > 0 is true, <= 0 is false• Definitions end in semicolons• Block has a single expression

• Function literals have syntax { ident, ident, ... => block }

Some SL1 Examples

▪ Simple computation:

val a = 3;val b = 4;a * a + b * b

▪ A function literal and a function call

val max = { x, y => if (x - y) x else y };max(3, 4)

▪ A higher-order function

val threeTimes = { f, x => f(f(f(x))) };threeTimes({x => x * x}, 2)

▪ A recursive function

def fac = { x => if (x) x * fac(x - 1) else 1 };fac(10)

Interpreter Outline

▪ Parse the program, converting into Expr (as before) / Block▪ Block is sequence of definitions, followed by one expression▪ Need to add capabilities to eval(Expr)

• if / else• function call

▪ Evaluating block:• Add definitions to symbol table• Evaluate expression with that table

The Symbol Table

▪ We had a lab (in this lecture) with evaluating variable definitions▪ In that lab, we used a Map[String, Int] for the symbol table.▪ Issue #1: Not all values are of type Int (i.e. functions)▪ Remedy: Use Any▪ Issue #2: We may have duplicate variable definitions

val a = 3;val fun = { x => val a = 2; a * x };a * fun(4)

▪ Remedy: Use a List[(String, Any)] instead▪ To add a definition, use ::▪ Newer definitions at the front shadow the older ones

((a, 2), (x, 4), (a, 3))

▪ To look up a symbol's value, use find to find the first match:

symbols.find(_._1 == name) match {case Some(pair) => pair._2case None => None

}

Evaluating Expressions

▪ eval of an expression yields a value▪ Need symbol table for looking up variables▪ Result may be either an Int or a function

val a = 3 * b;val sq = { x => x * x };

def eval(expr : Expr, symbols : List[(String, Any)]) : Any =expr match {

case Number(num) => numcase Variable(name) => ... // find name in symbolscase Operator(left, right, f) => ...

// was f(left, right), but now we don't know they are Intcase IfExpr(cond, block1, block2) => ......

}

Evaluating Blocks

▪ Block = list of definitions + one expression▪ Evaluate all definitions▪ Add pairs to symbol table

def evalDef(symbols : List[(String, Any)], defn : Valdef) =(defn.name, eval(defn.expr, symbols)) :: symbols

▪ (Additional wrinkle with function definitions—later)▪ Use resulting table to evaluate expression

def evalBlock(block : Block, symbols : List[(String, Any)]) : Any =eval(block.expr, (symbols /: block.defs) { evalDef(_, _) } )

Visualize foldLeft with two definitions: (red fringe = list ofdefs)

symbolsfor expr

/ \augmented 2ndsymbols def/ \

original 1stsymbols def

Parsing Challenge: Function Calls

▪ Lookahead required to distinguish between variable andfunction(args)

▪ Parse with optional parameter list▪ If found, generate a Funcall, else a Variable

def valOrFuncall = valOrFun ~ opt( "(" ~> repsep(expr, ",") <~ ")" ) ^^ {case expr ~ Some(args) => Funcall(expr, args)case expr ~ None => expr

}

▪ Lookahead problem isn't limited to names. { x => x * x } is afunction literal, { x => x * x }(3) is a function call.

def valOrFun = "(" ~> expr <~ ")" |ident ^^ { Variable(_) } |funliteral

def funliteral: Parser[Expr] = ("{" ~> repsep(ident, ",") <~ "=>") ~ expr <~ "}" ^^ {

case params ~ expr => Function(params, expr)}

▪ Note: repsep(ident, ",")matches comma-separated list of ident,yielding a List[String] (with the separators discarded)

Calling a Function

▪ Inputs:• Function with parameter list and block, e.g. { x, y => (x + y) / 2 }• List of parameters, e.g. (10, a + 2)

▪ Add bindings (param name, param value) to symbol table

x -> eval(10)

y -> eval(a + 2)

▪ Evaluate body with that symbol table

def eval(expr : Expr, symbols : List[(String, Any)]) : Any =expr match {

case Funcall(fun, args) => eval(fun, symbols) match {case Function(params, body) =>

evalBlock(body, params.zip(args.map(eval(_, symbols))) ::: symbols)}

▪ params.zip(args) makes a list of pairs ((p1, a1), (p2, a2), ...)▪ But we need to evaluate the arguments. The map takes care of

that▪ Parser and interpreter has < 150 lines of code!▪ Still need to discuss what to do with recursive function—next

class

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab11/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: Becoming Comfortable with SL1

1. Make a project lab11 and add this Scala code. Run the four SL1 programsfrom slide 3. What outputs do you get?

2. Write a SL1 program that defines pow2 to compute 2n for a given n, then callspow2(10). What is your program and what is its output?

Step 2: Understanding the Parse Tree

For each of the following SL1 expressions and blocks, draw a parse tree. Thatshould be the tree returned by parser.parse(...).get. Write the tree sidewayswith indentations, like this for the expression x * (y + 1). Use - for indentationsso they don't get lost in your report.

Feel free to modify the program to print the parse tree.

Operator-Variable(x)-Operator--Variable(y)--Number(1)--<add function>-<multiply function>

1. { x, y => x + y }2. fac(x - 1)3. val sq = { x => x * x } ; sq(2)

Step 3: Understanding Symbol Tables

For each of the following scenarios, write the symbol table. Write it as a set ofequations, like this

a = 3sq = { x => x * x }a = 2

where the newest symbols appear at the bottom. In our example, the last definitionof a shadows the previous one. Note that each variable is bound either to aninteger or a function.

1. At the end of the program val a = 3 ; val b = a + 1; val a = 2 ; a + b2. At the execution of the function call in the program val a = 3; val sq = { x

=> x * x } ; sq(a + 2). Be sure to show the binding for x.3. At the execution of the function call in the program val a = 3; val x = 2; {

x => a * x } (a)

Confirm your guesses for 2 and 3 with this trick. Define a function

def spy[T](t : T) = { println(t); t }

Then add a spy(...) call around

params.zip(args.map(eval(_, symbols))) ::: syms

in the evaluation of a Funcall.

CS 152 - Lecture 13

Cay S. Horstmann

www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.

Scope

▪ Scope of named entity = region where name references the entity▪ Scopes can overlap

▪ class Example // Java{private int foo;public void fun(double foo) {

System.out.println(foo); // Which foo?}public void fun() {

System.out.println(foo); // Which foo?}

}

▪ Particularly common in languages with nested functions

def fun(x : Int) { // Scaladef helper() { var x : Int; ... }

...}

Static vs. Dynamic Scoping

▪ Static scope: Depends on program source only▪ Dynamic scope: Depends on execution history▪ Example

val x = 1;def f(y) = x + y;def g() { val x = 2; f(x) };g()

▪ Static scoping: Result is 1 + 2▪ Dynamic scoping: Result is 2 + 2▪ Dynamic scoping is common in interpreted languages (see lab)▪ Disadvantage: Meaning of program harder to understand

Implementation of Dynamic Scoping

▪ Global symbol table▪ Push definition when it is encountered, pop at end of scope▪ Example:

val x = 1;def f(y) = x + y;def g() { val x = 2; f(x) };g()

x -> 1 // first def. of xx -> 2, x -> 1 // def. of x inside gy -> 2, x -> 2, x -> 1 // def. of parameter y

Implementation of Static Scoping

▪ Each function has its own symbol table▪ Records the meanings of symbols when the function was defined▪ Function body is evaluated with that table (augmented by

parameter bindings)▪ Example:

val x = 1;def f(y) = x + y; // x -> 1def g() { val x = 2; f(x) }; // f -> ...

Table only needs to store free variables in function body

▪ When f is executed:

y -> 2, x -> 1

Implementing Recursive Functions

▪ Can't use val:

val fac = x => if (x == 0) 1 else x * fac(x - 1)

▪ RHS is an anonymous function with free variable fac

x => if (x == 0) 1 else x * fac(x - 1)

▪ Then a new variable fac is defined▪ def simultaneously adds fac and RHS to symbol table

case class Closure(params : List[String], body : Block, var env : List[(String, Any)])...

def evalDef(symbols : List[(String, Any)], defn : Definition) =defn match {

case Defdef(name, Function(params, body)) => {val cl = Closure(params, body, symbols)val syms = (name, cl) :: symbolscl.env = syms // mutation

syms}...

}

▪ Here, for the first time, we use a var in Scala.▪ Pure functional solution is possible but complex (Google for Y

combinator).

Lab

▪ You work with a buddy▪ One of you writes the code (coder), the

other types up answers (scribe)▪ When you get stuck, ask your buddy

first!▪ Switch roles each lab. The previous

scribe is the coder for this lab.▪ The scribe submits lab work in lab12/report.txt inside the Git

repo. Include the coder's name in the report!▪ If the coder wants to submit something, that's cool too. Do

whatever is easy.

Step 1: Bash

▪ Launch your bash shell.▪ Next, some background information. The bash command shell has

a rudimentary programming language. Here are some of itsfeatures.

1. You declare global variables with the syntax

varname=initialValue

2. You declare functions with the syntax

function funname {. . .

}

Function parameters are not named. Instead, you refer to them as$1, $2, $3...

3. You call functions with the syntax

funname arg1 arg2 arg3 . . .

4. You declare local variables (inside functions) with the syntax

local varname=initialValue

5. You get the value of a variable with the syntax

$varname

For example, the command

echo $a

prints the value of a

Step 1: Bash (Continued)

1. Make a file test1.bash with the contents

x="2"

function f {echo $x

}

function main {local x="3" ;f ;

}

main

.2. Start the bash shell. Run the program: source test1.bash. What does it

print?3. What does that tell you about variable scoping in the bash language?

Step 2: More Bash

1. Make a file test2.bash with the contents.

x="2"

function f {echo $x

}

function g {local x="3" ;f ;

}

function main {local x="4" ;f ;g ;f ;

}

f ;main

What do you expect this program to print?

2. Start the bash shell. Run the program: source test2.bash. What does itprint?

3. Explain the behavior by drawing diagrams of the symbol table.

Step 3: Understanding Closures in SL1

1. Consider the SL1 program

val a = 3;val f = { x => x * a };val g = { a => a * f(a)};g(1)

What result output do you expect?

2. Start the SL1 interpreter from the previous lab and run the program. Whatoutput did you get?

3. Place a breakpoint in the line

case Valdef(name, Function(params, body)) => { (name, Closure(params, body, symbols)) :: symbols }

Run the debugger with the same program as input. When the breakpointis hit, inspect symbols. What do you get for symbols when the definitionsof f and g are evaluated?

4. Change the code for

case Funcall(fun, args) => eval(fun, symbols) match

to

case Funcall(fun, args) => val funval = eval(fun, symbols); funval match

Debug the program again, this time with a breakpoint in the line

evalBlock(body, params.zip(args.map(eval(_, symbols))) ::: syms)

The breakpoint will be triggered when g and f are executed. What arethe values of symbols and the syms in the closure object when g isexecuted?

5. With what symbol table is the body of f executed?