Lab 6: Lazy
- Deadline: 26 March, 2024, Tuesday, 23:59, SGT
- Difficulty Level: 9
Prerequisite
- Caught up to Unit 32 of Lecture Notes
- Completed Lab 5
Files
The following functional interfaces are already provided:
- cs2030s.fp.Combiner
- cs2030s.fp.Transformer
- cs2030s.fp.BooleanCondition
- cs2030s.fp.Producer
- cs2030s.fp.Consumer
Copy your implementation of cs2030s.fp.Maybe Exercise 5 over before you get started with Exercise 6. A skeleton for Lazy<T> as well as a wrongly-implemented copy of LazyList.java is provided.
The files Test1.java, Test2.java, etc., as well as CS2030STest.java, are provided for testing. You can edit them to add your test cases, but they will not be submitted.
Lazy
Programming languages such as Scala support lazy values, where the expression that produces a lazy value is not evaluated until the value is needed. Lazy value is useful for cases where producing the value is expensive, but the value might not eventually be used. Java, however, does not provide a similar abstraction. So, you are going to build one.
This task is divided into several stages. You are highly encouraged to read through all the stages to see how the different levels are related.
You are required to design a single Lazy class as part of the cs2030s.fp package with two fields. You are not allowed to add additional fields to Lazy.
1 2 3 4 5 6 | |
Take note of the following constraints:
- Avoid using the protected
Maybe::getmethod and avoid access the classesMaybe.Some<T>orMaybe.Nonedirectly. - Since
Maybehas internalizedif-elsechecks for whether the value is there or not, you must not use any form of conditional statements to compare ifvaluecontains something or not. - You are not allowed to use any raw types or
@SuppressWarnings
The Basics of Being Lazy
Define a generic Lazy class to encapsulate a value with the following operations:
- static
of(T v)method that initializes theLazyobject with the given value. - static
of(Producer<? extends T> s)method that takes in a producer that produces the value when needed. get()method that is called when the value is needed. If the value is already available, return that value; otherwise, compute the value and return it. The computation should only be done once for the same value.toString(): returns"?"if the value is not yet available; returns the string representation of the value otherwise.
Note that for our class to be immutable and to make the memorization of the value transparent, toString should call get() and should never return "?". We break the rules of immutability and encapsulation here, just so that it is easier to debug and test the laziness of your implementation.
Hint: You may find the method valueOf from the class String useful.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
You can test your code by running the Test1.java provided. The following should compile without errors or warnings. Make sure your code follows the CS2030S Java style and can generate the documentation without error.
1 2 3 4 | |
Map and FlatMap
Now let's add the map and flatMap method. Remember that Lazy should not evaluate anything until get() is called, so the function f passed into Lazy through map and flatMap should not be evaluated until get() is called. Furthermore, they should be evaluated once. That result from map and flatMap, once evaluated, should be cached (also called memoized), so that function must not be called again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
You can test your code by running the Test2.java provided. The following should compile without errors or warnings. Make sure your code follows the CS2030S Java style and can generate the documentation without error.
1 2 3 4 | |
Filter
Write a filter method, which takes in a BooleanCondition and lazily tests if the value passes the test or not. Returns a Lazy<Boolean> object. The BooleanCondition must be executed at most once.
Then write an equals, which overrides the equals method in the Object class. equals is an eager operation that causes the values to be evaluated (if not already cached). equals should return true only both objects being compared are Lazy and the value contains within are equals (according to their equals() methods).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
You can test your code by running the Test3.java provided. The following should compile without errors or warnings. Make sure your code follows the CS2030S Java style and can generate the documentation without error.
1 2 3 4 | |
Combine
We have provided an interface called Combiner<S, T, R> in cs2030s.fp, with a single combine method to combine two values, of type S and T respectively, into a result of type R.
Add a method called combine into Lazy. The combine method takes in another Lazy object and a Combiner implementation to lazily combine the two Lazy objects (which may contain values of different types) and return a new Lazy object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
You can test your code by running the Test4.java provided. The following should compile without errors or warnings. Make sure your code follows the CS2030S Java style and can generate the documentation without error.
1 2 3 4 | |
Lazy List
The Lazy class can be used to build a lazy-evaluated list.
Consider the class EagerList below. Given n, the size of the list, seed, the initial value, and f, an operation, we can generate an EagerList as [seed, f(seed), f(f(seed)), f(f(f(seed))), ... ], up to n elements.
We can then use the method get(i) to find the i-th element in this list, or indexOf(obj) to find the index of obj in the list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
But suppose f() is an expensive computation, and we ended up just needing to get(k) where k is much smaller than N, then, we would have wasted our time computing all the remaining elements in the list! Similarly, if the obj that we want to find using indexOf is near the beginning of the list, there is no need to compute the remaining elements of the list.
Change the EagerList class into a new class called LazyList, making use of the Lazy class you have constructed, so that get() and indexOf() causes evaluation of f() only as many times as necessary. Hint: you only need to make minimal changes. Neither a new field nor a new loop is necessary.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
You can test your code by running the Test5.java provided. The following should compile without errors or warnings. Make sure your code follows the CS2030S Java style and can generate the documentation without error.
1 2 3 4 | |
Following CS2030S Style Guide
You should make sure that your code follows the given Java style guide.