Homework 8. What To Turn In. Reading. Self Check. Problems. Handout 19 CSCI 334: Spring, 2017

Similar documents
CS107 Handout #24 Autumn /16/98 Assignment 1c: Random Sentence Generator This assignment adapted from an original concept by Mike Cleron.

Grammars and Parsing, second week

Introduction to Programming Using Java (98-388)

Lab 4 Due April 18 th

Homework 7. What To Turn In. Reading. Self Check. Handout 18 CSCI 334: Spring, 2017

Outline. Java Models for variables Types and type checking, type safety Interpretation vs. compilation. Reasoning about code. CSCI 2600 Spring

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

GRAMMARS & PARSING. Lecture 7 CS2110 Fall 2013

Homework 9 CSCI 334: Spring, 2019

If you are going to form a group for A2, please do it before tomorrow (Friday) noon GRAMMARS & PARSING. Lecture 8 CS2110 Spring 2014

CS107 Handout 04 Spring 2007 April 4, 2007 Assignment 1: Random Sentence Generator

CS 330 Homework Comma-Separated Expression

Tiny Compiler: Back End

MP 3 A Lexer for MiniJava

CS107 Handout 37 Spring 2007 May 25, 2007 Introduction to Inheritance

CS164: Programming Assignment 2 Dlex Lexer Generator and Decaf Lexer

Lecture 21: The Many Hats of Scala: OOP 10:00 AM, Mar 14, 2018

Defining Program Syntax. Chapter Two Modern Programming Languages, 2nd ed. 1

UNIVERSITY OF CALIFORNIA Department of Electrical Engineering and Computer Sciences Computer Science Division. P. N. Hilfinger

Inheritance, Polymorphism, and Interfaces

Subclass Gist Example: Chess Super Keyword Shadowing Overriding Why? L10 - Polymorphism and Abstract Classes The Four Principles of Object Oriented

CS 4240: Compilers and Interpreters Project Phase 1: Scanner and Parser Due Date: October 4 th 2015 (11:59 pm) (via T-square)

Assignment #6: Random Sentence Generator Due: Friday, March 12 by 5pm

Lab 10: Sockets 12:00 PM, Apr 4, 2018

CS Exam 1 Review Suggestions

Compilers and computer architecture: Semantic analysis

CS2112 Fall Assignment 4 Parsing and Fault Injection. Due: March 18, 2014 Overview draft due: March 14, 2014

The Decaf Language. 1 Lexical considerations

JAVA MOCK TEST JAVA MOCK TEST II

Overriding methods. Changing what we have inherited from e.g. Object

Project 1: Scheme Pretty-Printer

Notes from a Short Introductory Lecture on Scala (Based on Programming in Scala, 2nd Ed.)

Grammars & Parsing. Lecture 12 CS 2112 Fall 2018

Java Programming Lecture 7

CSCI 3155: Lab Assignment 6

CSCI 3155: Lab Assignment 2

MP 3 A Lexer for MiniJava

Data Structures (list, dictionary, tuples, sets, strings)

Lab 9: More Sorting Algorithms 12:00 PM, Mar 21, 2018

Compiler principles, PS1

CS/ENGRD 2110 SPRING 2018

Programming Assignment II

Topic 7: Algebraic Data Types

AP Computer Science Chapter 10 Implementing and Using Classes Study Guide

Overview. Elements of Programming Languages. Objects. Self-Reference

ADTS, GRAMMARS, PARSING, TREE TRAVERSALS

16 Multiple Inheritance and Extending ADTs

AP COMPUTER SCIENCE JAVA CONCEPTS IV: RESERVED WORDS

Zhifu Pei CSCI5448 Spring 2011 Prof. Kenneth M. Anderson

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

IC Language Specification

CSCI 136 Written Exam #1 Fundamentals of Computer Science II Spring 2014

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

CSE 143: Computer Programming II Summer 2017 HW4: Grammar (due Tuesday, July :30pm)

Programming Assignment I Due Thursday, October 7, 2010 at 11:59pm

Inheritance. Transitivity

Executive Summary. It is important for a Java Programmer to understand the power and limitations of concurrent programming in Java using threads.

Compilers. Compiler Construction Tutorial The Front-end

A programming language requires two major definitions A simple one pass compiler

15CS45 : OBJECT ORIENTED CONCEPTS

CSCI 3155: Homework Assignment 4

INCORPORATING ADVANCED PROGRAMMING TECHNIQUES IN THE COMPUTER INFORMATION SYSTEMS CURRICULUM

Programming in Scala Second Edition

ADTS, GRAMMARS, PARSING, TREE TRAVERSALS

Homework #10 due Monday, April 16, 10:00 PM

Programming Assignment I Due Thursday, October 9, 2008 at 11:59pm

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

+ Inheritance. Sometimes we need to create new more specialized types that are similar to types we have already created.

Inheritance and Polymorphism

Starting to Program in C++ (Basics & I/O)

INHERITANCE. Spring 2019

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

Programming Project 1: Lexical Analyzer (Scanner)

ADTS, GRAMMARS, PARSING, TREE TRAVERSALS

CSCI 3155: Lab Assignment 6

Jim Lambers ENERGY 211 / CME 211 Autumn Quarter Programming Project 4

G Programming Languages - Fall 2012

Semantic Analysis and Type Checking

syntax tree - * * * - * * * * * 2 1 * * 2 * (2 * 1) - (1 + 0)

Example: Count of Points

Inheritance and Encapsulation. Amit Gupta

CSE wi Final Exam 3/12/18. Name UW ID#

Object-oriented Compiler Construction

Assignment 5. Introduction

The Java Language The Java Language Reference (2 nd ed.) is the defining document for the Java language. Most beginning programming students expect

COMPILER CONSTRUCTION LAB 2 THE SYMBOL TABLE. Tutorial 2 LABS. PHASES OF A COMPILER Source Program. Lab 2 Symbol table

CMSC 330: Organization of Programming Languages

CSE 331 Software Design and Implementation. Lecture 14 Generics 2

Object-Oriented Design Lecture 14 CSU 370 Fall 2007 (Pucella) Friday, Nov 2, 2007

CSCI 200 Lab 4 Evaluating infix arithmetic expressions

Project 6 Due 11:59:59pm Thu, Dec 10, 2015

The design of the PowerTools engine. The basics

CS143 Handout 05 Summer 2011 June 22, 2011 Programming Project 1: Lexical Analysis

This course supports the assessment for Scripting and Programming Applications. The course covers 4 competencies and represents 4 competency units.

Using Scala in CS241

CS 2340 Objects and Design - Scala

CSE 413 Final Exam Spring 2011 Sample Solution. Strings of alternating 0 s and 1 s that begin and end with the same character, either 0 or 1.

Rules and syntax for inheritance. The boring stuff

Program Fundamentals

F1 A Java program. Ch 1 in PPIJ. Introduction to the course. The computer and its workings The algorithm concept

Transcription:

Homework 8 Due 25 April Handout 19 CSCI 334: Spring, 2017 What To Turn In Please hand in work in two pieces, one for the Problems and one for the Programming (Partner Optional): Problems: Turn in handwritten or typed answers by the due date. Be sure your work is stapled and that your answers are clearly marked and in the correct order. Programming: Turn in a printout of your code separately from your problem answers. You are welcome to work with a partner. If you do, only one person needs to hand in a printout, but be sure both names are at the top of the file. Turn in an electronic copy as well using the instructions at the end of the programming questions. Reading 1. (Required) Mitchell, Chapters 12 2. (As Needed) Scala Resources Self Check S1...................................................... Assignment and Derived Classes Mitchell, Problem 12.1 I put a working version of the code on the handouts page if you would like to experiment with it. Use g++ to compile the program, and then run the executable a.out with the command./a.out. Problems Q1. (16 points)......................................................... Function Subtyping Assume that Square <: Rectangle and Rectangle <: Polygon. Which of the following subtype relationships hold in principle? (a) (Square Square) <: (Rectangle Rectangle) (b) (Square Rectangle) <: (Rectangle Rectangle) (c) (Square Polygon) <: (Rectangle Rectangle) (d) (Rectangle Square) <: (Rectangle Rectangle) (e) (Rectangle Rectangle) <: (Rectangle Rectangle) (f) (Rectangle Polygon) <: (Rectangle Rectangle) (g) (Polygon Square) <: (Rectangle Rectangle) (h) (Polygon Rectangle) <: (Rectangle Rectangle) 1

(i) (Polygon Polygon) <: (Rectangle Rectangle) (j) (Square (Rectangle Square)) <: (Polygon (Rectangle Square)) (k) (Rectangle (Rectangle Square)) <: (Polygon (Rectangle Square)) (l) (Polygon (Rectangle Square)) <: (Polygon (Rectangle Square)) (m) (Polygon (Square Square)) <: (Polygon (Rectangle Square)) (n) (Polygon (Polygon Rectangle)) <: (Polygon (Rectangle Square)) (o) (Polygon (Polygon Square)) <: (Polygon (Rectangle Square)) (p) (Rectangle (Rectangle Rectangle)) <: (Polygon (Rectangle Square)) Q2. (10 points)............................................................................ Printing Programmers often want to print user-defined object types in a reader-friendly way to output streams (like System.out in Java). For example, we may wish to print a Point object p as (3,4). There are many ways to design a programming system to facilitate this. (a) One not-so-great way is to have the stream class provide methods to print each kind of object. For example, the OutputStream class for whatever language we are using could be defined to have a println(point p) method to facilitate writing the user-defined Point class to an output stream, and similar methods for other object types. What is the deficiency with this approach? (b) In C++, this problem of writing user-defined types in a reasonable way is solved through operator overloading. C++ stream classes use the << operator to write to streams. For example, cout << 4 writes the number 4 to the terminal. To define how user-defined types are written to streams, one defines a new function like the following: class Point { int x, y;... ; ostream& operator<<(ostream& o, Point p) { return o << "(" << p.x << "," << p.y << ")" ; With this definition, executing cout << p, would print (3,4) if p were a Point object with those coordinates. Java does not use operator overloading. The method public void println(object o) {... from java.io.printstream permits one to call System.out.println(o) on any object o and print its representation. How does the language and library ensure that this method can print different types of objects in the appropriate way? What methods must the programmer write, how are classes structured, and what language features are involved? (c) Could the designers have taken the same approach in C++? Would that approach fit the C++ design criteria? 2

Pair Programming P1. (30 points).......................................................... Generics and Traits Consider the following trait (included in the starter code, Sets.scala): trait AbstractSet[T] { def contains(n: T): Boolean; def add(n: T): AbstractSet[T]; def remove(n: T): AbstractSet[T]; def addall(lst: Iterable[T]): AbstractSet[T] = { lst.foldleft(this)((s, elem) => s.add(elem)); def removeall(lst: Iterable[T]): AbstractSet[T] = { lst.foldleft(this)((s, elem) => s.remove(elem)); Traits are almost like Java interfaces. They can specify methods that concrete classes must implement in order to mix-in the trait. In this example, the AbstractSet trait has three methods that concrete set classes must implement: a method to determine membership (contains), a method to add a member (add), and a method to remove a member (remove). Note that traits can also be parameterized by types. In this case, the AbstractSet is parameterized by the type T of elements stored in the set. Unlike Java interfaces, traits can also implement methods. These methods are included (for free) in any class that mixes in the trait. The AbstractSet trait has two such methods: a method to add an entire List of members (addall) and a method to remove an entire List of members (removeall). These methods are implemented in the trait itself, even though they rely on methods that have no implementation yet. (The methods addall and removeall are written using fold, although you need not be concerned about the details.) (a) Write a concrete class MutableSet that extends the AbstractSet trait and implements the three methods that that trait requires. As its name suggests, your class should be mutable. That is, calling the add or remove methods on a MutableSet should modify the instance of the set on which the method was called, such that it now contains (or no longer contains), the specified element. Note that the AbstractSet trait specifies that the add and remove methods must return an AbstractSet. In the case of MutableSet, it is sufficient to return the current set (this). This has been implemented for you in the starter code. (Remember that you typically do not use the keyword return; any code block automatically returns its last expression.) There are many ways to implement MutableSet. This assignment is purposefully openended. You may want to use collections like List[T] or Array[T] from the standard library. (The standard library also includes Set collections, but please don t cheat and implement your MutableSet with a Set from the standard library.) Regardless of your implementation choices, the starter code includes some unit tests that your MutableSet should pass. (b) Write a concrete class ImmutableSet that extends the AbstractSet trait and implements the three methods that that trait requires. As its name suggests, your class should be immutable. That is, calling the add or remove methods on an ImmutableSet should not modify the instance of the set on which the method was called. Instead, the method should return a new set which contains all the previous elements plus the added element (or minus the removed element). Hopefully this motivates the return type requirements of the AbstractSet trait. 3

Implementing immutable structures can be a little annoying/tricky. Here s one example that illustrates how to define multiple constructors in Scala, which may be useful to you. The following Counter is a functional counter that supports a bump method that returns a new counter who s value is one greater than the original counter. The class has a private default constructor that takes a parameter for the counter s initial value and a public constructor that takes no parameters. Clients may only use the public constructor, meaning all counters must start at 0. The bump method uses the private constructor to create a new counter object with an initial value other than 0: class Counter private (val initial : Int) { // public constructor def this() = this(0); // return a new Counter with value one greater than mine def bump() : Counter = new Counter(initial + 1); override def tostring() : String = initial.tostring; As with MutableSet, the starter code includes some unit tests that your ImmutableSet should pass. (c) Since Scala allows method definitions in traits and sets no limit on how many traits can be mixed in, this provides for something akin to multiple inheritance. Consider the following trait: trait LockingSet extends AbstractSet { abstract override def add(n: Int): AbstractSet = synchronized { super.add(n) abstract override def remove(n: Int): AbstractSet = synchronized { super.remove(n) abstract override def contains(n: Int): Boolean = synchronized { super.contains(n) The LockingSet trait can be mixed into any AbstractSet to create a set that can safely be used in the presence of multiple threads. The synchronized keyword works as you would expect from Java: each AbstractSet instance has a lock that must be acquired before the synchronized method can be performed. Given this definition, a thread-safe MutableSet can be instantiated as follows: val s = new MutableSet with LockingSet Now write a trait LoggingSet that extends the AbstractSet trait and provides logging functionality. Your trait should write to standard output (using the println function) each time the contains, add or remove methods are called. Now a MutableSet that is both thread-safe and logs its usage to standard output can be instantiated as follows: val s = new MutableSet with LoggingSet with LockingSet Note that our definitions for LockingSet and LoggingSet are agnostic to the actual implementation of our set. These traits can just as easily and without modification be mixed into our ImmutableSet class. (d) Do you expect there to be a difference between MutableSet with LoggingSet with LockingSet and MutableSet with LockingSet with LoggingSet? What might that difference be? (e) Does it make sense to mix the LockingSet trait into an ImmutableSet? Why or why not? (f) Please turn in your Sets.scala code with turnin. 4

P2. (60 points)......................................... Random Sentence Generator The goals of this problem are to: (a) write a class hierarchy in Scala, and (b) utilize the Composite Design Pattern. Random Sentence Generator. The Random Sentence Generator creates random sentences from a grammar. With the right grammar you could, for example, use this program to generate homework extension requests: Wear down the Professor s patience: I need an extension because I used up all my paper and then my dorm burned down and then I didn t know I was in this class and then I lost my mind and then my karma wasn t good last week and on top of that my dog ate my notes and as if that wasn t enough I had to finish my doctoral thesis this week and then I had to do laundry and on top of that my karma wasn t good last week and on top of that I just didn t feel like working and then I skied into a tree and then I got stuck in a blizzard on Mt. Greylock and as if that wasn t enough I thought I already graduated and as if that wasn t enough I lost my mind and in addition I spent all weekend hung-over and then I had to go to the Winter Olympics this week and on top of that all my pencils broke. Plead innocence: I need an extension because I forgot it would require work and then I didn t know I was in this class. Honesty: I need an extension because I just didn t feel like working. Grammars. The program reads in grammars written in a form illustrated by this simple grammar file to generate poems: <start> = The <object> <verb> tonight ; <object> = waves big yellow flowers slugs ; <verb> = sigh <adverb> portend like <object> die <adverb> ; <adverb> = warily grumpily ; The strings in brackets (<>) are the non-terminals. Each non-terminal definition is followed by a sequence of productions, separated by characters, and with a ; at the end. Each production consists of a sequence of white-space separated terminals and non-terminals. A production may be empty so that a non-terminal can expand to nothing. There will always be whitespace surrounding the, =, and ; characters to make parsing easy. Here are two possible poems generated by generating derivations for this grammar: 5

The big yellow flowers sigh warily tonight The slugs portend like waves tonight Your program will create a data structure to represent a grammar it reads in and then produce random derivations from it. Derivations will always begin with the non-terminal <start>. To expand a non-terminal, simply choose one of its productions from the grammar at random and then recursively expand each word in the production. For example: <start> -> The <object> <verb> tonight -> The big yellow flowers <verb> tonight -> The big yellow flowers sigh <adverb> tonight -> The big yellow flowers sigh warily tonight System Architecture. A grammar consists of terminals, non-terminals, productions, and definitions. These four items have one thing in common: they can all be expanded into a random derivation for that part of a grammar. Thus, we will create classes organized in the following class hierarchy to store a grammar: GrammarElement Terminal NonTerminal Production Definition The abstract class GrammarElement provides the general interface to all pieces of a grammar. It is defined as follows: abstract class GrammarElement { /** * Expand the grammar element as part of a random * derivation. Use grammar to look up the definitions * of any non-terminals encountered during expansion. */ def expand(grammar : Grammar) : String; /** * Return a string representation of this grammar element. * This is useful for debugging. (Even though we inherit a * default version of tostring() from the Object superclass, * I include it as an abstract method here to ensure that * all subclasses provide their own implmementaiton.) */ def tostring() : String; The Grammar object passed into expand is used to look up the definitions for non-terminals during the expansion process, as described next. The Grammar Class. A Grammar object maps non-terminal names to their definitions. At a minimum, your Grammar class should implement the following: 6

class Grammar { // add a new non-terminal, with the given definition def +=(nt : String, defn : Definition) // look up a non-terminal, and return the definition, or null // if not def exists. def apply(nt : String) : Definition // Expand the start symbol for the grammar. def expand() : String // return a String representation of this object. override def tostring() : String The tostring method is useful for debugging. Subclasses. The four subclasses of GrammarElement represent the different pieces of the grammar and describe how each part is expanded: Terminal: A terminal just stores a terminal string (like slugs ), and a terminal expands to itself. NonTerminal: A non-terminal stores a non-terminal string (like <start> ). When a nonterminal expands, it looks up the definition for its string and recursively expands that definition. Production: A production stores a list of GrammarElements. To expand, a production simply expands each one of these elements. Definition: A definition stores a vector of Productions. A definition is expanded by picking a random Production from its vector and expanding that Production. This design is an example of the Composite Design Pattern. The hierarchy of classes leads to an extensible design where no single expand method is more than a few lines long. Implementation Steps. (a) Download the starter code from the handouts web page. Once compiled with scalac (or fsc), you will run the program with a command like scala RandomSentenceGenerator < Poem.g You will need to use Scala s generic library classes. In particular, you will probably want to use both Lists and Maps from the standard Scala packages. The full documentation for these classes is accessible from the cs334 links web page. (b) Begin by implementing the four subclasses of GrammarElement. Do not write expand yet, but complete the rest of the classes so that you can create and call tostring() on them. (c) The next step is to parse the input to your program and build the data structure representing the grammar in RandomSentenceGenerator.java. The grammar will be stored in the instance variable grammar. I have provided a skeleton of the parsing code. The parser uses a java.util.scanner object to perform lexical analysis and break the input into individual tokens. I use the following two Scanner methods: i. String next(): Removes the next token from the input stream and returns it. 7

ii. boolean hasnext(string pattern): Returns true if and only if the next token in the input matches the given pattern. (If pattern is missing, this will return true if there are any tokens left in the input.) When parsing the input, it is useful to keep in mind what form the input will have. particular, we can write an EBNF grammar for the input to your program as follows: <Grammar> ::= [ Non-Terminal = <Definition> ; ]* <Definition> ::= <Production> [ <Production> ]* <Production> ::= [ <Word> ]* where Non-Terminal is a non-terminal from the grammar being read and Word is any terminal or non-terminal from the grammar being read. Recall that the syntax [ Word ]* matches zero or more Words. The parsing code follows this definition with the following three methods: protected def readgrammar(in : Scanner): Grammar protected def readdefinition(in : Scanner): Definition protected def readproduction(in : Scanner): Production Modify these methods to create appropriate Terminal, NonTerminal, Production, Definition, and Grammar objects for the input. You may wish to print the objects you are creating as you go to ensure the grammar is being represented properly. You will need to complete the definition of Grammar at this point as well. (d) Once the grammar can be properly created and printed, implement the expand methods for your GrammarElements. Scala provides a random number generator that can be used as follows: val number = Random.nextInt(N); // number is in range [0,N-1]. Change RandomSentenceGenerator to create and print three random derivations after printing the grammar. (e) Submit your Scala code with turnin as usual, and include a printout of the code with your written homework. (f) You may also submit new a new grammar if you like. It can be as simple or complicated as you like. Bonus points for creativity. A few details about producing derivations: The grammar will always contain a <start> non-terminal to begin the expansion. It will not necessarily be the first definition in the file, but it will always be defined eventually. I have provided some error checking in the parsing code, but you may assume that the grammar files are otherwise syntactically correct. The one error condition you should catch reasonably is the case where a non-terminal is used but not defined. It is fine to catch this when expanding the grammar and encountering the undefined non-terminal rather than attempting to check the consistency of the entire grammar while reading it. The starter code contains a RandomSentenceGenerator.fail(String msg) method that you can call to report an error and stop. When generating the output, just print the terminals as you expand. Each terminal should be preceded by a space when printed, except the terminals that begin with punctuation like periods, comma, dashes, etc. You can use the Character.isLetterOrDigit method to check whether a character is punctuation mark. This rule about leading spaces is just a rough heuristic, because some punctuation (quotes for example) might look better with spaces. Don t worry about the minor details we re looking for something simple that is right most of the time and it s okay if is little off for some cases. In 8

What To Turn In for the Programs. Submit a printout of your files separate from the written homework. (If you worked in a pair, only one person should do this.) Also, submit electronic copies with the command turnin -c 334 file, where file is the name of the file you wish to submit. You may submit files more than once if you find a mistake or wish to change what you submitted the first time. Again, only one of each pair needs to submit the code. 9