Computer Science CSC324 Wednesday February 13, Homework Assignment #3 Due: Thursday February 28, 2013, by 10 p.m.

Similar documents
A Brief Introduction to Standard ML

CSE 341, Autumn 2005, Assignment 3 ML - MiniML Interpreter

A Concepts-Focused Introduction to Functional Programming Using Standard ML Sample Exam #1 Draft of August 12, 2010

CSE 341, Spring 2011, Final Examination 9 June Please do not turn the page until everyone is ready.

Part I: Written Problems

CMSC 330: Organization of Programming Languages

CSC324- TUTORIAL 5. Shems Saleh* *Some slides inspired by/based on Afsaneh Fazly s slides

Recap: Functions as first-class values

A quick introduction to SML

Programming Languages

CSC/MAT-220: Lab 6. Due: 11/26/2018

Begin at the beginning

A Second Look At ML. Chapter Seven Modern Programming Languages, 2nd ed. 1

News. Programming Languages. Recap. Recap: Environments. Functions. of functions: Closures. CSE 130 : Fall Lecture 5: Functions and Datatypes

CSC324 Functional Programming Typing, Exceptions in ML

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

Scope and Introduction to Functional Languages. Review and Finish Scoping. Announcements. Assignment 3 due Thu at 11:55pm. Website has SML resources

Lists. Michael P. Fourman. February 2, 2010

L3 Programming September 19, OCaml Cheatsheet

CSCI-GA Final Exam

CSE341 Spring 2017, Final Examination June 8, 2017

Functional programming Primer I

Specification and Verification in Higher Order Logic

CSE341 Autumn 2017, Final Examination December 12, 2017

CSE341 Spring 2017, Final Examination June 8, 2017

Variables and Bindings

Hard deadline: 3/28/15 1:00pm. Using software development tools like source control. Understanding the environment model and type inference.

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

CSE341, Spring 2013, Final Examination June 13, 2013

COMP 105 Homework: Type Systems

Flang typechecker Due: February 27, 2015

Assignment 4: Evaluation in the E Machine

CSE341 Spring 2016, Final Examination June 6, 2016

Recap from last time. Programming Languages. CSE 130 : Fall Lecture 3: Data Types. Put it together: a filter function

OCaml. ML Flow. Complex types: Lists. Complex types: Lists. The PL for the discerning hacker. All elements must have same type.

Assignment 1. University of Toronto, CSC384 - Introduction to Artificial Intelligence, Winter

CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures. Dan Grossman Autumn 2018

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

Programming Languages

Mini-ML. CS 502 Lecture 2 8/28/08

CSE341 Section 3. Standard-Library Docs, First-Class Functions, & More

Lecture 3: Recursion; Structural Induction

Handout 2 August 25, 2008

The type checker will complain that the two branches have different types, one is string and the other is int

Currying fun f x y = expression;

CSE 341 Section 5. Winter 2018

Functional Programming

Homework 6. What To Turn In. Reading. Problems. Handout 15 CSCI 334: Spring, 2017

Programming Assignment 2

COMP 105 Assignment: Hindley-Milner Type Inference

Metaprogramming assignment 3

Fall Lecture 3 September 4. Stephen Brookes

Homework 4 Due Friday, 10/3/08

CSE341: Programming Languages Lecture 7 First-Class Functions. Dan Grossman Winter 2013

More Assigned Reading and Exercises on Syntax (for Exam 2)

(Refer Slide Time: 4:00)

CSE 143: Computer Programming II Spring 2015 HW7: 20 Questions (due Thursday, May 28, :30pm)

Project 1: Scheme Pretty-Printer

Sample Exam; Solutions

Implementing nml: Hindley-Milner Type Inference

To figure this out we need a more precise understanding of how ML works

An Introduction to Functions

Repetition Structures

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

Condition-Controlled Loop. Condition-Controlled Loop. If Statement. Various Forms. Conditional-Controlled Loop. Loop Caution.

Part I: Written Problems

Programming Languages

SCHEME AND CALCULATOR 5b

Recap. Recap. If-then-else expressions. If-then-else expressions. If-then-else expressions. If-then-else expressions

Recap: ML s Holy Trinity. Story So Far... CSE 130 Programming Languages. Datatypes. A function is a value! Next: functions, but remember.

Programming Languages

CSE 130 Programming Languages. Datatypes. Ranjit Jhala UC San Diego

CS 312 Problem Set 5: Concurrent Language Interpreter

CS 275 Name Final Exam Solutions December 16, 2016

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

Recap: ML s Holy Trinity

CPS 506 Comparative Programming Languages. Programming Language Paradigm

CS 1803 Pair Homework 3 Calculator Pair Fun Due: Wednesday, September 15th, before 6 PM Out of 100 points

Plan (next 4 weeks) 1. Fast forward. 2. Rewind. 3. Slow motion. Rapid introduction to what s in OCaml. Go over the pieces individually

UNIVERSITY OF TORONTO Faculty of Arts and Science. Midterm Sample Solutions CSC324H1 Duration: 50 minutes Instructor(s): David Liu.

Compilers Project 3: Semantic Analyzer

Tail Recursion: Factorial. Begin at the beginning. How does it execute? Tail recursion. Tail recursive factorial. Tail recursive factorial

SML A F unctional Functional Language Language Lecture 19

Lecture 2: SML Basics

Inductive Data Types

CSCI 3155: Homework Assignment 3

1.3. Conditional expressions To express case distinctions like

Structure and Interpretation of Computer Programs

15 150: Principles of Functional Programming. More about Higher-Order Functions

Data Abstraction. An Abstraction for Inductive Data Types. Philip W. L. Fong.

Semantic Analysis. CSE 307 Principles of Programming Languages Stony Brook University

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

CVO103: Programming Languages. Lecture 5 Design and Implementation of PLs (1) Expressions

Repetition Through Recursion

Chapter 15. Functional Programming Languages

CMSC 330: Organization of Programming Languages. Operational Semantics

CS52 - Assignment 8. Due Friday 4/15 at 5:00pm.

Module 8: Local and functional abstraction

Introduction to SML Getting Started

A general introduction to Functional Programming using Haskell

Transcription:

Computer Science CSC324 Wednesday February 13, 2013 St. George Campus University of Toronto Homework Assignment #3 Due: Thursday February 28, 2013, by 10 p.m. Silent Policy A silent policy takes effect 24 hours before this assignment is due. This means that no question about this assignment will be answered, whether it is asked on the forum, by email, or in person. How you will be marked There are 100 marks available in this assignment. Your solutions will be marked with respect to correctness, style, efficiency, readability, and quality of documentation and testing. The file cover.txt contains the detailed breakdown of the marks. This assignment counts for 10% of the course grade. Handing in this Assignment For this assignment, you need to submit the following files: cover.txt evaluator.sml questions.txt tree.sml fix.sml cover.txt can be found in /u/csc324h/winter/pub/a3/. To submit a file named filename.ext electronically, execute the following command on cdf: submit -c csc324h -a a3 -f filename.ext Type man submit for more information. Warning: Marks will be deducted for incorrect submission. Note that without a properly completed and signed cover page (simply type your name in the Signature field), your assignment will not be marked. Warning: Your code must run in SML/NJ on CDF to be assigned a mark. No exceptions. Assignment 3 Announcements and Discussion Board Important clarifications to the assignment will be posted on the course webpage. You are also responsible for monitoring the CSC324 forum. Getting Started with SML 1. To start SML on CDF, just type sml. You will see the SML prompt -, and you can type in ML code and the interpreter immediately evaluates it and produces output. 2. To quit SML, simply press Ctrl-D (under Windows press Ctrl-Z). 3. To load your code from a file: - use "myfile.sml"; 4. To determine or change the current directory: - OS.FileSys.getDir() - OS.FileSys.chDir("path") 5. Comments in SML are specified as: (* comments start here *) 1

6. A very useful reference for understanding SML error messages is: http://www.smlnj.org/doc/errors.html ML Coding Guidelines Use Pattern Matching. The use of pattern matching, whenever possible, is considered good style. For example, rather than writing: fun sum L = if null L then 0 else hd L + sum (tl L); it is better to write: fun sum [] = 0 sum (h::t) = h + sum t; Documentation. Each function you write, including the helper functions, should have a concise and clear description of the function, the argument, preconditions, postconditions, return value, and the type of the function. Do not include the type in the preconditions, but provide it in a separate statement in the comment for the function. Any assumptions you make about the form of a function s inputs/outputs (other than their type) should be included in the preconditions. Style and Indentation. We should be able to tell the structure of your function by looking at it. Use indentation appropriately. In general, you should write clear and readable code (for which you will need to use your own judgement). Pick informative names for your helper functions and variables. For multiline comments, the following is considered good style: (* comments * comments * comments * comments *) Efficiency. Your code should be reasonably efficient (though not necessary optimal). For example, you should not evaluate the same expression multiple times, or traverse lists unnecessarily. Also, do not create unneeded variables or bind then unnecessarily. Question 1. Datatypes (Submitted Filename: evaluator.sml). For this question, you will write a mathematical expression evaluator for a simple calculator language. Your evaluator will take a program in the form of parsed abstract syntax, and execute it, producing an integer resulting from performing all of the mathematical operations (including applications of user-defined functions) designated by the program. You can assume that the users program has been parsed into an abstract syntax represented by the datatype Expr, as follows: type Name = string; datatype Expr = Const of int Var of Name 2

Neg of Expr Plus of Expr * Expr Mult of Expr * Expr App of Fun * Expr and Fun = Def of Name * Expr Each element of type Expr represents a parsed expression of our simple calculator language. An expression can be a constant integer, addition or multiplication of two expressions, the negation of an expression, or the application of a function to an expression. Functions are defined by a variable bound by the function and an expression representing the functions body. For example, the Scheme function (lambda (x) (* 2 (+ x 1))) would be represented as: Def("x", Mult(Const 2, Plus(Var "x", Const 1))) where the definition Def is simply a function of one variable, that binds this one variable (whose name is given as the first argumnet of the constructor Def) within the body of the definition (the expression that is given as the second argument of Def). In the subquestions below, you will write functions for evaluating expressions in our simple calculator language. a. Define a function substitute: (Name, Expr, Expr) -> Expr, that takes a tuple of three elements: n of type Name, e1 of type Expr, and e2 of type Expr. substitute returns a new expression, that is the result of replacing every occurrence of Var n in e2 by expression e1. for example: (* substitute every Var "x" in expression Var "x" by expression Var "y". *) - substitute ("x", Var "y", Var "x"); val it = Var "y" : expr (* substitute every Var "z" in expression Var "x" by expression Var "y". There is none. *) - substitute ("z", Var "y", Var "x"); val it = Var "x" : expr (* substitute every "z" in expression Const 1 by expression Var "y". There is none. *) - substitute ("z", Var "y", Const 1); val it = Const 1 : Expr (* substitute every Var "x" in expression Mult(Var "x", Const 3) by expression Const 2. *) - substitute ("x", Const 2, Mult(Var "x", Const3)); val it = Mult(Const 2, Const 3) : Expr (* substitute every Var "z" in expression Neg(Plus(Var "x", Var "z")) by expression Var "y". *) - substitute ("z", Var "y", Neg(Plus(Var "x", Var "z"))); val it = Neg(Plus(Var "x", Var "y")) : Expr (* substitute every Var "z" in expression App(Def("x", Plus(Var "z", Var "x")), Const 3) by expression Neg(Const 1). *) - substitute ("z", Neg(Const 1), App(Def("x", Plus(Var "z", Var "x")), Const 3); val it = App(Def("x", Plus(Neg(Const 1), Var "x")), Const 3) : Expr Note: You should assume the following preconditions for the function substitute(n, e1, e2): 3

1. n is not bound by any function-definition within e2. 2. No variable appearing in e1 is bound by a function-definition in e2. 3. Definitions in e2 have fresh variable names, that is: no name is bound by more than one definition in e2, and that no name bound by a definition appears outside the body of the definition. Thus, the following example is not a valid use of substitute because it results in an invalid expression, i.e., Def("x", Plus(Var "y", Var "z")). Note that this use of substitute actually violates the first precondition above. - substitute("x", Var "y", Def("x", Plus(Var "x", Var "z"))); b. Write an evaluator for closed expressions of type Expr(see below for a definition of closed expressions). You should write a function eval: Expr -> int that reduces a given closed expression to an integer by evaluating the expression. Constants cannot be further simplified/reduced, and the mathematical operators Plus, Mult, and Neg are evaluated in the obvious fashion, so that: - eval (Const 2); val it = 2 : int - eval (Plus ((Const 2), (Const 3))); val it = 5 : int - eval (Neg (Mult ((Const 2), (Const 3)))); val it = ~6 : int Function applications, App(f,e), are evaluated by evaluating the body of the applied function (f: Fun) after substituting every instance of the function s bound variable with the value of the second part of the function application. For example: - eval (App (Def ("x", Plus (Var "x", Const 2)), (Const 3))); val it = 5 : int (* Note: the initial expression is equal to eval (Plus (Const 3, Const 2)). *) - eval (App (Def ("x", Plus (Var "x", Var "x")), Plus (Const 12, Const 9))); val it = 42 : int (* Note: the initial expression is equal to eval (Plus (Plus (Const 12, Const 9), Plus (Const 12, Const 9))) - eval (App (Def ("x", App (Def ("y", Plus(Var "x", Var "y")), Var "x")), Const 3)); val it = 6 : int (* Note: the initial expression is equal to eval (App (Def ("y", Plus (Const 3, Var "y")), Const 3)), which is equal to: eval (Plus (Const 3, Const 3)) *) A closed expression is one that does not contain variables that are not mentioned in the function definitions within the expression. If an expression is not closed, then your evaluator function should raise an exception, EvalException. For example, evaluating the following should raise an exception: 4

- eval (Var "x"); - eval (Plus (Var "x", Const 3)); - eval (App (Def ("y", Plus(Var "y", Var "x")), Const 3)); Question 2. Answering Questions (Submitted Filename questions.txt). Notice that our tiny programming language in Question (1) is not completely functional, in the sense that functions cannot be passed as parameters to functions, or returned from functions. Answer each of the following questions: a. What about the datatype Expr prevents returning functions as values? b. Briefly state how we can fix this problem. c. Write a new datatype declaration NewExpr in which functions are first-class expressions that may be returned from functions and passed as arguments to functions. Do not worry about enforcing correctness of expressions in the datatype declaration (i.e., What happens if I try to add two functions?, or What happens if I try to apply something that isn t a function, etc.). These are what a type-checker would do if we had one for our tiny language. Hint: You should only have to add one line to Expr and change one line already in Expr. Question 3. Recursive Datatypes; Higher-Order Functions (Submitted Filename: tree.sml). Consider the following datatype: datatype a tree = Node of a * a tree list; that represents a polymorphic tree, in which each node can have any number of children. In other words, each node in tree has a label (that can be of any type but real), followed by a list of its children, each representing a tree itself. For example, the following are instances of an int tree: Node(1, []) representing a single node with the label 1 and no children. Node(2, [ Node(1, []), Node(3, []) ]) representing a node (with label 2) and two children: one a node with the label 1, and the other a node with the lable 3. Node(6, [ Node(2, [ Node(1, []), Node(3, []) ]), Node(4, [ Node(5, []), Node(7, []) ]) ]) representing the following visually-represented tree: 6 / \ 2 4 / \ / \ 1 3 5 7 5

In the subquestions below, you will write functions for peforming certain operations on a tree. a. Write a function count: a * a tree -> int that takes an element x and a tree t, and returns the number of times that x appears as a node label in t. Do not use higher-order procedures (HOPs). Use recursion. b. Now write the same function as in 3(a), but using HOPs. Call this counthop. c. Write a function depth: a tree -> int, which takes a tree and returns its depth: i.e., the maximum length of a path from the root of the tree to a leaf. Do not use HOPs. Use recursion. d. Now write the same function as in 3(c) using HOPs. Call this depthhop. Question 4. Functions as Return Values; Currying (Submitted Filename: fix.sml). We call x a fixed point of function f if f(x) = x. In the following subquestions, you will write variations of a function fix: ( a -> a) -> ( a -> a) that takes a unary function f as input, and returns a function that applies f to its argument repeatedly until a fixed point is reached, i.e., until ((f x) = x). Note that, the functions returned by fix may not terminate for all inputs. For example, (fix (fn x => ~x)) never reaches a fixed point if we apply it to anything other than 0. You should not worry about such cases (assume they are supposed to run forever!). Here are some examples of two unary functions (mytl and halve), and their use with fix: fun mytl [] = [] mytl (h::r) = r fun halve x = x div 2; - (fix mytl) []; val it = []:?.X1 list - (fix mytl) [1, 2, 3]; val it = [] : int list - (fix halve) 2; val it = 0 : int - (fix halve) ~10; val it = ~1 : int a. Name your function fixa, and write it using a globally-defined helper function. fixa should not be recursive, and the helper function should not return a function. b. Name your function fixb, and write it using recursion and no helper function. Note that fixb is recursive, and that each recursive call returns an unnamed function expression. c. Name your function fixc, and write it using no helper functions and no unnamed functions. You will need to write fixc using a curried function definition of the following form: fun fixc f x =... 6