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::get
method and avoid access the classesMaybe.Some<T>
orMaybe.None
directly. - Since
Maybe
has internalizedif-else
checks for whether the value is there or not, you must not use any form of conditional statements to compare ifvalue
contains 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 theLazy
object 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.