INTERPRETATION AND SCHEME LISTS 12

Similar documents
INTERPRETATION AND SCHEME LISTS 12

61A Lecture 26. Monday, October 31

Interpreters and Tail Calls Fall 2017 Discussion 8: November 1, 2017 Solutions. 1 Calculator. calc> (+ 2 2) 4

SCHEME AND CALCULATOR 5b

INTERPRETERS AND TAIL CALLS 9

EXCEPTIONS, CALCULATOR 10

INTERPRETERS 8. 1 Calculator COMPUTER SCIENCE 61A. November 3, 2016

Public-Service Announcement

Functional Programming. Pure Functional Programming

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

Discussion 12 The MCE (solutions)

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

A Brief Introduction to Scheme (II)

Principles of Programming Languages 2017W, Functional Programming

Fall 2018 Discussion 8: October 24, 2018 Solutions. 1 Introduction. 2 Primitives

Spring 2018 Discussion 7: March 21, Introduction. 2 Primitives

CS61A Midterm 2 Review (v1.1)

Fall 2017 Discussion 7: October 25, 2017 Solutions. 1 Introduction. 2 Primitives

Documentation for LISP in BASIC

Intro. Scheme Basics. scm> 5 5. scm>

SCHEME The Scheme Interpreter. 2 Primitives COMPUTER SCIENCE 61A. October 29th, 2012

6.184 Lecture 4. Interpretation. Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson

User-defined Functions. Conditional Expressions in Scheme

Why do we need an interpreter? SICP Interpretation part 1. Role of each part of the interpreter. 1. Arithmetic calculator.

Building a system for symbolic differentiation

61A LECTURE 18 SCHEME. Steven Tang and Eric Tzeng July 24, 2013

SCHEME 10 COMPUTER SCIENCE 61A. July 26, Warm Up: Conditional Expressions. 1. What does Scheme print? scm> (if (or #t (/ 1 0)) 1 (/ 1 0))

Scheme in Scheme: The Metacircular Evaluator Eval and Apply

CALCULATOR Calculator COMPUTER SCIENCE 61A. July 24, 2014

Announcements. The current topic: Scheme. Review: BST functions. Review: Representing trees in Scheme. Reminder: Lab 2 is due on Monday at 10:30 am.

Building a system for symbolic differentiation

Streams, Delayed Evaluation and a Normal Order Interpreter. CS 550 Programming Languages Jeremy Johnson

CS 61A Interpreters, Tail Calls, Macros, Streams, Iterators. Spring 2019 Guerrilla Section 5: April 20, Interpreters.

Syntactic Sugar: Using the Metacircular Evaluator to Implement the Language You Want

Lecture 09: Data Abstraction ++ Parsing is the process of translating a sequence of characters (a string) into an abstract syntax tree.

6.001 Notes: Section 15.1

Introduction to Scheme

(scheme-1) has lambda but NOT define

Fall Semester, The Metacircular Evaluator. Today we shift perspective from that of a user of computer langugaes to that of a designer of

6.037 Lecture 4. Interpretation. What is an interpreter? Why do we need an interpreter? Stages of an interpreter. Role of each part of the interpreter

15 Unification and Embedded Languages in Lisp

CS 360 Programming Languages Interpreters

Modern Programming Languages. Lecture LISP Programming Language An Introduction


CS 61A, Fall, 2002, Midterm #2, L. Rowe. 1. (10 points, 1 point each part) Consider the following five box-and-arrow diagrams.

Project 2: Scheme Interpreter

CS 61A Discussion 8: Scheme. March 23, 2017

Functional Programming. Pure Functional Languages

Typed Racket: Racket with Static Types

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal)

Sample Final Exam Questions

An Explicit-Continuation Metacircular Evaluator

Scheme: Strings Scheme: I/O

CS 314 Principles of Programming Languages

Scheme. Functional Programming. Lambda Calculus. CSC 4101: Programming Languages 1. Textbook, Sections , 13.7

Administrivia. Simple data types

4.2 Variations on a Scheme -- Lazy Evaluation

Lecture #24: Programming Languages and Programs

Using Symbols in Expressions (1) evaluate sub-expressions... Number. ( ) machine code to add

STRUCTURE AND INTERPRETATION COMPUTER PROGRAMS >>> COMPUTER SCIENCE IN THE NEWS. CS61A Lecture 23 Py TODAY REVIEW: INTERPRETATION 7/26/2012 READ PRINT

STRUCTURE AND INTERPRETATION COMPUTER PROGRAMS COMPUTER SCIENCE IN THE NEWS. CS61A Lecture 23 Py TODAY 7/26/2012

(Func&onal (Programming (in (Scheme)))) Jianguo Lu

C311 Lab #3 Representation Independence: Representation Independent Interpreters

INTRODUCTION TO SCHEME

TAIL RECURSION, SCOPE, AND PROJECT 4 11

Common LISP Tutorial 1 (Basic)

Comp 311: Sample Midterm Examination

11/6/17. Functional programming. FP Foundations, Scheme (2) LISP Data Types. LISP Data Types. LISP Data Types. Scheme. LISP: John McCarthy 1958 MIT

Functional Programming. Pure Functional Languages

MORE SCHEME. 1 What Would Scheme Print? COMPUTER SCIENCE MENTORS 61A. October 30 to November 3, Solution: Solutions begin on the following page.

Computer Science 21b (Spring Term, 2015) Structure and Interpretation of Computer Programs. Lexical addressing

Symbolic Programming. Dr. Zoran Duric () Symbolic Programming 1/ 89 August 28, / 89

COMPUTER SCIENCE IN THE NEWS. CS61A Lecture 21 Scheme TODAY REVIEW: DISPATCH DICTIONARIES DISPATCH DICTIONARIES 7/27/2012

CS 275 Name Final Exam Solutions December 16, 2016

Scheme Tutorial. Introduction. The Structure of Scheme Programs. Syntax

CS61A Lecture 23 Py. Jom Magrotker UC Berkeley EECS July 26, 2012

RLISTS AND THE ENVIRONMENT MODEL 9

Scheme: Data. CS F331 Programming Languages CSCE A331 Programming Language Concepts Lecture Slides Monday, April 3, Glenn G.

A LISP Interpreter in ML

61A LECTURE 17 ORDERS OF GROWTH, EXCEPTIONS. Steven Tang and Eric Tzeng July 23, 2013

Below are example solutions for each of the questions. These are not the only possible answers, but they are the most common ones.

CSc 520 Principles of Programming Languages

The Typed Racket Guide

Structure and Interpretation of Computer Programs

Functional programming in LISP

A First Look at ML. Chapter Five Modern Programming Languages, 2nd ed. 1

From Syntactic Sugar to the Syntactic Meth Lab:

CSE341 Spring 2017, Final Examination June 8, 2017

6.037 Lecture 7B. Scheme Variants Normal Order Lazy Evaluation Streams

Review Analyzing Evaluator. CS61A Lecture 25. What gets returned by mc-eval? (not analyzing eval) REVIEW What is a procedure?

Introduction to lambda calculus Part 3

COP4020 Programming Assignment 1 - Spring 2011

Pairs and Lists. (cons 1 2) 1 2. (cons 2 nil) 2 nil. Not a well-formed list! 1 > (cdr x) 2 > (cons 1 (cons 2 (cons 3 (cons 4 nil)))) ( ) (Demo)

;; definition of function, fun, that adds 7 to the input (define fun (lambda (x) (+ x 7)))

Deferred operations. Continuations Structure and Interpretation of Computer Programs. Tail recursion in action.

Common LISP-Introduction

INF4820: Algorithms for Artificial Intelligence and Natural Language Processing. Common Lisp Fundamentals

LECTURE 16. Functional Programming

CSE341 Autumn 2017, Final Examination December 12, 2017

Transcription:

INTERPRETATION AND SCHEME LISTS 12 COMPUTER SCIENCE 61A July 26, 2012 1 Calculator Language We are beginning to dive into the realm of interpreting computer programs. To do so, we will examine a few new programming languages. The Calculator language, a very simple language and invented for this class, is the first of these examples. Our Calculator language (or Calc ) looks very similar to Python, except it only handles four arithmetic operations: add, sub, mul, div, and the corresponding symbols (+, -, *, and /). All of these operators are prefix operators, which means that the operator precedes the operands that it is operating on. Here are a few examples of Calculator in action: calc> 6 6 calc> add(3, 5) 8 calc> mul() 1 calc> add(1, mul(3, sub(3, 7))) -11 Our goal now is to write an interpreter for the Calculator language. The job of an interpreter is, given an expression, perform all the steps necessary to evaluate it. The interpreter that we saw in lecture spends most of its time in the read-eval-print-loop (often called a REPL), which continuously reads a line of input, translates this into an expression object (we call this parsing the input), evaluates that expression object, and prints the result. Nearly all interpreters can be organized into this REPL pattern! 1

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 2 We are primarily interested in the E part of REPL, which stands for evaluation. 1.1 Representing and Evaluating Expressions When we type a line at the Calculator prompt and hit enter, we have just sent an expression to the interpreter. We can represent an expression as an object: class Exp(Object): def init (self, operator, operands): self.operator = operator self.operands = operands def repr (self): return "Exp({0}, {1})".format(repr(self.operator), repr(self.operands))) def str (self): operand_strs = ", ".join(map(str, self.operands)) return "{0}({1})".format(self.operand, operand_strs) The most important thing to note is that every Exp is represented by its operator and operands. Also, another thing to note: the operands can also be Exp objects themselves. In fact, this representation of an expression is called an expression tree, since the children of the operator are also Exp objects. For example, if we wanted to represent the Calculator expression add(2, 3), we would call the Exp constructor as follows: Exp("add", [2, 3]). 1.2 Evaluating Expressions Our Calculator language is only concerned with two kinds of expressions: numbers, which are self-evaluating expressions, and call expressions, which involve an operator acting on some number of arguments, each of which are themselves expressions. What does it mean for a number to be self-evaluating? If we evaluate an expression that is a number, the value of the expression (or the result returned by evaluating it) is that number. What about evaluating call expressions? We follow this two-step process: 1. Evaluate the operands. 2. Apply the operator to the arguments (the values of the operands). Using this two-step process, we can interpret any Calculator expression. The first step will be handled by a function called calc_eval, and the second step will be handled by a function called calc_apply. It is defined as:

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 3 def calc_eval(exp): if type(exp) in (int, float): # If the expression is a number, return exp # Return the number. else: # Evaluate the operands. arguments = list(map(calc_eval, exp.operands)) # Apply the function to the operands. return calc_apply(exp.operator, arguments) As you can see, all we have done is follow the rules of evaluation outlined above. If the expression is a number, return it. Else, evaluate the operands and apply the operator on the evaluated operands. How do we apply the operator? We use calc_apply: def calc_apply(operator, args): if operator in ("add", "+"): return sum(args) if operator in ("sub", "-"): if len(args) == 0: raise TypeError(operator + " needs at least 1 arg") if len(args) == 1: return -args[0] return sum(args[0], [-args for args in args[1:]]) if operator in ("mul", "*"): return reduce(mul, args, 1) if operator in ( div, / ): if len(args)!= 2: raise TypeError(operator + requires exactly 2 arguments ) numer, denom = args return numer/denom Depending on what the operator is, we can match it to a corresponding Python call. Each conditional in the function above corresponds to the application of one operator. Notice that calc_eval deals with expressions, while calc_apply deals with values. In general, when you want to add something to the calc language, you will want to edit one (or both) of calc_eval and calc_apply. Sometimes, it is possible to make changes in either function to get the same result, but it is important to consider which is more appropriate to edit. The general idea is: If you want to create a special rule for the order that we evaluate the operands of an expression, you will want to edit calc_eval.

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 4 However, if you want to simply create a new function (with no special rules for the way it is evaluated), then it is generally better to edit calc_apply instead. 1. How would we construct the Exp object that corresponds to the expression add(4, 5, mul(3, 2))? Solution: Exp("add", [4, 5, Exp("mul", [3, 2])]) 2. We want to allow Calc to perform exponentiation: >>> a = exp(2, 4) 16 >>> a = **(2, 4) Assuming that we have modified the parser to understand the new keywords exp and **, which of the two functions calc_eval and calc_apply would we modify to include this functionality, and how? Solution: We are adding a new operator, so we would modify the function calc_apply to recognize how this new operator works with its operand. We add the following line to calc_apply: def calc_apply(operator, args):... if operator in ("exp" or "**"): if len(args)!= 2: raise TypeError(operator + \ requires exactly 2 arguments ) base, exp = args return base**exp 3. How many calls to calc_eval and calc_apply are produced by the expressions below? (a) 5 Solution: 1 call to calc_eval and 0 calls to calc_apply. The number is self-evaluating, and there are no operators that we need to apply on to operands. (b) mul(5, 6)

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 5 Solution: 3 calls to calc_eval and 1 call to calc_apply. Two of the three calls to calc_eval are for each of the two numbers, and one more call for the entire expression. (c) mul(5, add(div(4, 1), 6)) Solution: 7 calls to calc_eval and 3 calls to calc_apply. There are as many calls to calc_eval as there are nodes in the expression tree. (Why?) (d) mul(sub(5, 1), add(4, 6)) Solution: 7 calls to calc_eval and 3 calls to calc_apply. 4. We want to add new functionality that would let us save values in variables: calc> save(a, 5) calc> mul(6, a) 30 Assuming that we have already modified the Calc parser to allow words to be operands, should we modify calc_apply, calc_eval, or both to add this new feature? Why, and how? (Hint: You may find it useful to keep a global dictionary.) Solution: We need to modify both calc_eval and calc_apply. In calc_apply, we need to understand the new operator save. To keep track of what variables correspond to what values, we will use a global dictionary that maps from variables to values, and applying the new save operator will add/update a binding in this global dictionary. vars_and_vals = {} def calc_apply(operator, args):... if operator == "save": if len(args)!= 2: raise TypeError(operator + \ requires exactly 2 arguments ) # Add/update the global dictionary of # variable-value mappings. var = args[0]

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 6 val = args[1] vars_and_vals[var] = [val] We also need to modify calc_eval to understand that, if it sees a string as an operand, then the operand must be a string. If so, it should evaluate that string to whatever value is bound to the same string in the global dictionary. def calc_eval(exp): if type(exp) in (int, float): return exp if type(exp) == str: if exp in vars_and_vals: return vars_and_vals[exp] raise KeyError( No binding for + exp + found. ) if type(exp) == Exp: arguments = list(map(calc_eval, exp.operands)) return calc_apply(exp.operator, arguments) 5. Louis Reasoner, while staying up all night in Soda, modified a line from his code for the Calc interpreter. In particular, he modified one line from calc_eval: def calc_eval(exp): if type(exp) in (int, float): return exp if type(exp) == Exp: # The line below was changed. arguments = exp.operands return calc_apply(exp.operator, arguments) Which of the following statements below will not be interpreted correctly? 1. 5 Solution: This will remain interpreted correctly, since numbers are self-evaluating. 2. add(5, 3) Solution: This will remain interpreted correctly, again because numbers are self-evaluating, so the original extra call to calc_eval would have produced the same result.

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 7 3. add(3, sub(2, 4)) Solution: This will produce an error, since the second operand has not been evaluated to provide the second argument to add. 2 Scheme: Pairs and Lists The basic data structure in Scheme is the pair. To construct a pair, we use the function cons: STk> (cons 3 4) (3. 4) Notice that Scheme represents a pair as the first element and the second element, separated with a dot. We can grab the first and the second elements of the list using the car and cdr function: STk> (car (cons 3 4)) 3 STk> (cdr (cons 3 4)) 4 Pairs can be used to construct larger data structures, such as (recursive) lists. In fact, Scheme is a dialect of Lisp, which is a name derived from List Processing. We can construct a list in many ways. The following expression should seem familiar to you from your adventures with IRLists and RLists. STk> (cons 1 (cons 2 (cons 3 ()))) (1 2 3) What is a list? Well, a list is a pair whose first element is the first element of the list, and the second element is the rest of the list. We thus construct a list by first making a pair, the first element of which is the first element of the list. The second element of this pair is the rest of this list, so we construct another list. We build another pair whose first element is now the second element of the entire list. We keep adding elements to the list in this fashion until we reach the end, when we have no more elements to add and we use the empty list (represented as () or nil). Notice that above, Scheme did not print a pair back at you. This is because Scheme recognizes that you are building a list, and displays it as such. You can also build lists using the list operator: STk> (list 1 2 3 4) (1 2 3 4)

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 8 or the quote operator: STk> (1 2 3 4) (1 2 3 4) As with any other pair, we can still use the function car and cdr: STk> (car (1 2 3 4)) 1 STk> (cdr (1 2 3 4)) (2 3 4) We can check if a list is empty using the null? function. STk> (null? (1)) #f STk> (null? ()) #t Note: (car x) is 2, (cdr x) is the list (3 4), and (car (cdr x)) is 3! Well, it is a bit tiresome to write (car (cdr x)) to get the second element of x. So Scheme, again the huggable lovable language that it is, provides a nifty shorthand: (cadr x). This reads cader, and means take the car of the cdr of. Similarly, you can use (caddr x) (pronounced caderder ) to take the car of the cdr of the cdr of the list x, which is 4. You can mix and match the a and d between the c and r to get the desired element of the list (up to four a or d). You can also append two lists together. append takes in any number of lists and outputs a list containing those lists concatenated together. So, (append (list 3 4) (list 5 6)) returns the list (3 4 5 6). 1. Assume that we have typed in the following definitions: (define u (cons 2 3)) (define v (list 2 3 4)) (define w (cons 5 6)) (define x (append v (list 2 5 6))) What do the following expressions evaluate to? (a) (car u) (b) (cdr u) (c) (+ (cdr u) (car w)) (d) (+ (car v) (cadr v)) (e) (cons 3 v)

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 9 We can also work with Scheme lists the same way we work with IRLists: treat the first and the rest of the list separately. For instance, here is the definition of a higher-order function map that takes in a function and returns a new list that results from applying that function on to each element of the list: (define (map fn lst) (if (null? lst) () (cons (fn (car lst)) (map fn (cdr lst))))) 1. Write the function add-one, which takes in a list and returns another list where 1 has been added to all of the elements. (define (add-one lst) Solution: (if (null? lst) () (cons (+ 1 (car lst)) (add-one (cdr lst))))) 2. Write the function num-satisfies that takes in a predicate function and a list, and returns the number of all the elements that satisfy the predicate. (define (num-satisfies pred lst) Solution: (if (null? lst) 0 (if (pred (car lst)) (+ 1 (num-satisfies pred (cdr lst))) (num-satisfies pred (cdr lst))))) 3. Write the function filter that takes in a predicate function (that returns #t or #f) and a list, and returns all the elements that satisfy the predicate. (define (filter pred lst)

DISCUSSION 12: INTERPRETATION AND SCHEME LISTS Page 10 Solution: (if (null? lst) () (if (pred (car lst)) (cons (car lst) (filter pred (cdr lst))) (filter pred (cdr lst))))) 4. Write a function remove that takes in an element and a list, and returns a new list where the element has been removed. (define (remove elem lst) Solution: (if (null? lst) () (if (= elem (car lst)) (remove elem (cdr lst)) (cons (car lst) (remove elem (cdr lst))))))