





Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Community
Ask the community for help and clear up your study doubts
Discover the best universities in your country according to Docsity users
Free resources
Download our free guides on studying techniques, anxiety management strategies, and thesis advice from Docsity tutors
Main points of this past exam are: Lexical Analyzer, Parentheses, Possibly Empty, Empty Lists, Grammar Suitable, Proposed Modification, Boolean Results, Procedure Shell, Shell Window, Implementation
Typology: Exams
1 / 9
This page cannot be seen from the preview
Don't miss anything!
Dec 3, 2009
This question asks you to develop an object system with prototype-based inheritance. You can (but don't have to) describe the solution you used in your project. This question also asks you to go beyond what you were required to implement in the project.
Part 1: Client code [9 points]
You may find it easier to first answer Part 2, where you implement the object system. In this part, you will use the system.
Write a code fragment in your 164 language that creates a prototype named Foo that contains fields x and y. Make the default values of x and y be 0.
def Foo = Object:new({ x = 0, y = 0 })
Write a code fragment that creates a prototype Bar that is a "subclass" of Foo and contains a field z. Make the default value of z be 1.
def Bar = Foo:new({ z = 1 })
Write a code fragment that creates an instance of Foo and an instance of Bar.
def foo = Foo:new({}) def bar = Bar:new({})
Add an instance method f(a) to Foo that returns the sum of x, y, and a.
Foo.f = lambda(self, a) { self.x + self.y + a }
Call the method f on your instance of Bar with the argument 42.
bar:f(42)
Part 4: Calling super [9 points]
Add to prototype Bar a method f(a) that adds the value of the field z to the value of the call to the same method in the superclass of Bar. The method f(a) must be written in your 164 language. Here is this method in Java:
class Bar extends Foo { ... int f(int a) { return super.f(a) + this.z; } ... }
Bar.f = lambda(self, a) { self:super().f(self, a) + self.z }
Add a constructor to Bar that calls the constructor of its superclass and then initializes z to the sum of x and y. In Java, this would look like:
class Bar extends Foo { ... Bar() { super(); this.z = this.x + this.y; } ... }
Bar.new = lambda(self) { def o = self:super().new(self, {}) o.z = o.x + o.y o }
In this question, you will build on your implementation of lists from Project 3. You will add the ability to concatenate lists.
Part 1 [6 points]
Consider the following program in the 164 language.
def range(min,max) { min = min - 1 lambda() { if (min < max) { min = min + 1 } else { null } } }
def v1 = [2*n for n in range(4,7)] // this is a list comprehension def v2 = [n/2 for n in v1]
One can implement lists as a special kind of dictionary, eg as a dictionary with a special key "length". (In contrast, our implementation defines a function length on dictionaries that returns the highest integer key in the dictionary.) The type of such a special dictionary can be considered to belist, hence we accepted "list" as a correct answer, too.
Part 2 [17 points]
Implement function concat1(x,y) that concatenates x and y. The arguments x and y could be either lists or iterators. The result of concat1(x,y) must be a list. If x and y are lists, these lists cannot be modified by concat1; instead, the result is a new list. Assume that append(lst,elmnt), for i in e { S }, list comprehensions and coroutines are available to you. You may not modify the interpreter. Example:
def lst = concat1(v1,range(0,1)) # lst has value [8, 10, 12, 14, 0, 1] print lst[2] # outputs 12
Your code for concat1:
The answer:
def concat1(l1,l2) { def l1copy = [e for e in l1] for e in l2 { append(l1copy,e) } l1copy }
Part 4 [10 extra credit points] This is a bonus question. Solve it only if you are done with the exam.
Consider the following code.
def lst2 = concat2(v1,v2) def x = lst2[1]
Because the loop in Part 3 iterates over the value returned by concat2, one can think of lst2 as a list. Yet the evaluation of lst2[1] fails. Why?
The value of lst2 is a function (an iterator). The operator [] cannot be applied on a function value, only on an dictionary value.
of the concatenation of v1 and v2 without modifying the interpreter:
Key idea: We allow the programmer to extend the default behavior of []. When [] is called on a function, we assume it is an iterator. We call the iterator idx-1 times, then return iterator().
More precisely, given an expression E1[E2], assume that iter is the value of E1 and idx is the value of E2. Assume that iter is a function value. We implement E1[E2] by calling iter idx-1 times, then returning iter().
Ideally, we should make a copy of the iterator iter, so that after iter[v] it is in the same position as it was before, but this is hard to do in general, so our suggested implementation does not do it. We settle for the semantics that indexing into an iterator with iter[v] advances the state of the iterator.
Step 1: Extend the language so that a program can add a hook to the operator []. This hook is a metamethod called __get. Once this metamethod is registered, whenever v1[v2] is attempted to be evaluated on value v1 that is not a dictionary, the interpreter calls __get(v1, v2).
We can decide to register this metamethod into the metatable of the global environment. In Lua, the global environment is a dictionary stored in variable _G.
setmetatable(_G,{__get=funGet}) # funGet is defined below.
Step 2: Define the hook. Here is where we implement the key idea describe above.
def funGet(fun,idx) {
while (idx>=0) { idx = idx - 1 fun() # last call to fun is what is returned from funGet } }
Consider the following Java program, which outputs "A A B". The first "A" indicates that, according to Java semantics, the methods B::f and C::f do not override the method A::f. Instead, they are considered to be unrelated methods (as if they were not even named f).
These methods are considered not to override A::f because the types of their parameters differ from that of A::f. As a result, an instance of class B has two methods: f(A x) and f(B x). The former is inherited from class A.
This question asks why Java's designers decided that B::f and C::f do not override A::f.
class A { int a; void f(A x) { System.out.println("A"); } }
class B extends A { int b; void f(B x) { System.out.println("B"); x.b = 100; } }
class C extends A { A c; void f(C x) { System.out.println("C"); } }
class Main { public static void main(String[] args) { A aa = new B(); aa.f(new B()); // in Java, this prints "A" B bb = new B(); bb.f(new A()); // in Java, this prints "A" bb.f(new B()); // in Java, this prints "B" // <your code for question 2 goes here> } }