-
This Practical Assessment consists of two questions. Answer ALL questions.
-
The total mark for this assessment is 30.
-
This is an OPEN BOOK assessment. You are only allowed to refer to written/printed notes. No online resources/digital documents are allowed. except those accessible from the PE nodes (peXXX.comp.nus.edu.sg) (e.g., man pages are allowed).
-
You should see the following in your home directory.
- The files Test1.java, Test2.java... Test5.java and CS2030STest.java for testing your solution.
- The skeleton files for Question 1:
Operand.java
InvalidOperandException.java
andOperation.java
- The skeleton files for Question 2:
EmptyList.java
,SourceList.java
,StringToLength.java
andPair.java
- The following files to solve Question 2 is provided for you:
IntegerToString.java
,GreaterThanTwo.java
,BooleanCondition.java
andTransformer.java
-
Solve the programming tasks by creating any necessary files and editing them. You can leave the files in your home directory and log off after the assessment is over. There is no need to submit your code.
-
Only the files directly under your home directory will be graded. Do not put your code under a subdirectory.
-
Write your student number on top of EVERY FILE you created or edited as part of the @author tag. Do not write your name.
Marking criteria:
- correctness (3 marks)
- design (10 marks)
- style (2 marks)
An expression is an entity that can be evaluated into a value.
We consider two types of expression in this question:
- An operand, which itself is a value.
- A binary operation, which is a mathematical function that takes in two expressions and produces an output value.
For instance,
- 3 is an expression that evaluates to 3.
- 3 + 2 is an expression that evaluates to 5
- (3 + 2) + 3 is also an expression that evaluates to 8
An operand is not necessarily an integer. It can be of any type. An expression can be evaluated to any type.
Three skeleton files are provided for you: Operand.java
, Operation.java
, and InvalidOperandException.java
. If you need extra classes or interfaces, create the necessary additional Java files yourself.
Create a class called Operand
that encapsulates the operands of an operation. The Operand
class can contain references to a value of any reference type.
You may create additional parent classes or interfaces if you think it is appropriate.
The Operand has an eval
method that returns its value.
jshell> new Operand(5).eval()
$.. ==> 5
jshell> new Operand("string").eval()
$.. ==> "string"
jshell> new Operand(true).eval()
$.. ==> true
Create an unchecked exception named InvalidOperandException
that behaves as follows:
jshell> InvalidOperandException e = new InvalidOperandException('!')
jshell> e.getMessage();
$.. ==> "ERROR: Invalid operand for operator !"
The constructor for InvalidOperandException
takes in a char
which is the corresponding symbol for the operation that is invalid.
Recall that all unchecked exceptions are a subclass of java.lang.RuntimeException
. The class RuntimeException
has the following constructor:
RuntimeException(String message)
that constructs a new runtime exception with the specified detail message message
. The message can be retrieved by the getMessage()
method.
You can test your code by running the Test1.java
provided. Make sure your code follows the CS2030S Java style.
$ javac Test1.java
$ java Test1
$ java -jar ~cs2030s/bin/checkstyle.jar -c ~cs2030s/bin/cs2030_checks.xml *.java
Create an abstract class called Operation
with the following fields and methods:
-
two private fields that correspond to two expressions (an expression is as defined at the beginning of this question).
-
a class factory method
of
, which returns the appropriate subclass that implements a specific operation. The first parameter of theof
methods is achar
to indicate the operation to be performed. You need to support three operations:- if the first parameter is
*
, return an operation that performs multiplication on integers - if the first parameter is
+
, return an operation that performs concatenation on strings - if the first parameter is
^
, return an operation that performs XOR on booleans - if the first parameter is none of the above, return
null
- if the first parameter is
Note that the operator to perform XOR on two boolean variables is ^
.
For instance,
jshell> Operation o = Operation.of('*', new Operand(2), new Operand(3));
jshell> o.eval()
$.. ==> 6
jshell> Operation o = Operation.of('+', new Operand("hello"), new Operand("world"));
jshell> o.eval()
$.. ==> "helloworld"
jshell> Operation o = Operation.of('^', new Operand(true), new Operand(false));
jshell> o.eval()
$.. ==> true
jshell> Operation.of('!', new Operand(2), new Operand(3));
$.. ==> null
jshell> Operation o1 = Operation.of('*', new Operand(2), new Operand(3));
jshell> Operation o = Operation.of('*', o1, new Operand(4));
jshell> o.eval()
$.. ==> 24
jshell> Operation o2 = Operation.of('*', new Operand(2), new Operand(4));
jshell> Operation o = Operation.of('*', o1, o2);
jshell> o.eval()
$.. ==> 48
If the operands are not of the correct type, eval
must throw an unchecked InvalidOperandException
exception.
For instance,
jshell> Operation o = Operation.of('*', new Operand("1"), new Operand(3));
jshell> try {
...> o.eval();
...> } catch (InvalidOperandException e) {
...> System.out.println(e.getMessage());
...> }
ERROR: Invalid operand for operator *
jshell> Operation o = Operation.of('+', new Operand(1), new Operand(4));
jshell> try {
...> o.eval();
...> } catch (InvalidOperandException e) {
...> System.out.println(e.getMessage());
...> }
ERROR: Invalid operand for operator +
jshell> Operation o = Operation.of('^', new Operand(false), new Operand(3));
jshell> try {
...> o.eval();
...> } catch (InvalidOperandException e) {
...> System.out.println(e.getMessage());
...> }
ERROR: Invalid operand for operator ^
jshell> Operation o1 = Operation.of('*', new Operand(1), new Operand(3));
jshell> Operation o2 = Operation.of('^', new Operand(false), new Operand(false));
jshell> Operation o = Operation.of('+', o1, o2);
jshell> try {
...> o.eval();
...> } catch (InvalidOperandException e) {
...> System.out.println(e.getMessage());
...> }
ERROR: Invalid operand for operator +
jshell> Operation o1 = Operation.of('*', new Operand(1), new Operand("3"));
jshell> Operation o2 = Operation.of('^', new Operand(false), new Operand(false));
jshell> Operation o = Operation.of('+', o1, o2);
jshell> try {
...> o.eval();
...> } catch (InvalidOperandException e) {
...> System.out.println(e.getMessage());
...> }
ERROR: Invalid operand for operator *
You can test your code by running the Test2.java
provided. Make sure your code follows the CS2030S Java style.
$ javac Test2.java
$ java Test2
$ java -jar ~cs2030s/bin/checkstyle.jar -c ~cs2030s/bin/cs2030_checks.xml *.java
Marking criteria:
- design (3 marks)
- correctness (10 marks)
- style (2 marks)
In this question, you will create a generic list using a Pair
class which is similar to what you saw in lectures. Note: This generic list is different and unrelated to the java.util.List
interface. We will not use that here.
We will call this generic list SourceList
(after the Source language used in the module CS1101S).
This question also uses the Transformer
and BooleanCondition
interfaces from Lab 4. The code for these two classes has been provided for you.
Remember the Pair
class from lectures with a first
and a second
value. The implementation of Pair
used in this question is different from that in the lectures, in that it has only one type parameter T
. Using this implementation it is possible to create a list using pairs: The generic list SourceList
is just a Pair
object, whose second value is either itself a Pair
object, or an EmptyList
object.
This chain of pairs constitutes a SourceList
. Each chain of pairs is terminated with an EmptyList
object.
Consider some examples:
jshell> SourceList<Integer> list = new EmptyList<>();
jshell> list
list ==> EmptyList
jshell> SourceList<String> list = new Pair<>("Hello", new EmptyList<>());
jshell> list
list ==> Hello, EmptyList
jshell> SourceList<Integer> list = new Pair<>(1, new Pair<>(2, new Pair<>(3, new EmptyList<>())));
jshell> list
list ==> 1, 2, 3, EmptyList
jshell> list.getFirst()
$.. ==> 1
jshell> list.getSecond().getFirst()
$.. ==> 2
jshell> list.getSecond().getSecond().getFirst()
$.. ==> 3
jshell>
You have been provided with the following skeletons:
- The
SourceList
interface:SourceList.java
- The
EmptyList
class:EmptyList.java
- The
Pair
class:Pair.java
Familiarise yourself with these files.
The Pair::toString
method has already been implemented for you. Read and understand how this method works by recursively calling the toString
method of the next pair in the pair chain.
You will now implement more methods for your SourceList
interface in the Pair
class.
An important SourceList
operation is to calculate the length of the list.
Implement the length
method which takes in no arguments and returns an int
which is the length of the list.
Consider some examples:
jshell> SourceList<String> strList = new Pair<>("AAA", new Pair<>("AA", new Pair<>("A", new EmptyList<>())))
jshell> strList.length()
$.. ==> 3
jshell> EmptyList<Integer> intEmpty = new EmptyList<>()
jshell> intEmpty.length()
$.. ==> 0
Implement the equals
method which takes in an argument of type Object
and returns a boolean
. This equals method should return true
when the two SourceList
are equal, and false
otherwise.
A SourceList
is equal if, in all Pairs
of the SourceList
, the first values are equal. All EmptyLists
are equal.
Consider the following example:
jshell> Object intList1 = new Pair<>(1, new Pair<>(2, new Pair<>(3, new EmptyList<>())));
jshell> Object intList2 = new Pair<>(1, new Pair<>(2, new Pair<>(3, new EmptyList<>())));
jshell> intList1.equals(intList1)
$.. ==> true
jshell> intList1.equals(intList2)
$.. ==> true
jshell> intList1.equals(new Pair<>("1", new Pair<>("2", new Pair<>("3", new EmptyList<>()))))
$.. ==> false
jshell> intList1.equals(new EmptyList<>())
$.. ==> false
jshell> new EmptyList<Integer>().equals(new EmptyList<String>())
$.. ==> true
jshell>
Note: you may only use one @SuppressWarnings
for this method. Nowhere else in your source code may you use any @SuppressWarnings
.
You can test your code by running the Test3.java
provided. Make sure your code follows the CS2030S Java style.
$ javac Test3.java
$ java Test3
$ java -jar ~cs2030s/bin/checkstyle.jar -c ~cs2030s/bin/cs2030_checks.xml *.java
Implement the filter
method which takes in a parameter of type BooleanCondition
and returns a new SourceList
containing only the elements which match the BooleanCondition
. Note: that this should create new pairs and not change the current SourceList
. Consider some examples:
jshell> SourceList<Integer> intList = new Pair<>(1, new Pair<>(2, new Pair<>(3, new Pair<>(4, new EmptyList<>()))));
jshell> intList.filter(new GreaterThanTwo())
$.. ==> 3, 4, EmptyList
jshell> intList
intList ==> 1, 2, 3, 4, EmptyList
jshell> intList.filter(new GreaterThanTwo()) == intList
$.. ==> false
jshell> SourceList<Integer> l = intList.filter(new GreaterThanTwo())
jshell> SourceList<String> l = intList.filter(new GreaterThanTwo())
| Error:
| incompatible types: SourceList<java.lang.Integer> cannot be converted to SourceList<java.lang.String>
| SourceList<String> l = intList.filter(new GreaterThanTwo());
| ^----------------------------------^
jshell> new EmptyList<Integer>().filter(new GreaterThanTwo())
$.. ==> EmptyList
jshell> SourceList<Integer> l = new EmptyList<Integer>().filter(new GreaterThanTwo())
jshell> SourceList<String> l = new EmptyList<Integer>().filter(new GreaterThanTwo())
| Error:
| incompatible types: SourceList<java.lang.Integer> cannot be converted to SourceList<java.lang.String>
| SourceList<String> l = new EmptyList<Integer>().filter(new GreaterThanTwo());
| ^---------------------------------------------------^
jshell> intList.filter(new GreaterThanTwo()).filter(new GreaterThanTwo());
$.. ==> 3, 4, EmptyList
jshell> new EmptyList<Integer>().filter(new GreaterThanTwo()).filter(new GreaterThanTwo())
$.. ==> EmptyList
Make sure your filter
method is as flexible as you can make it.
jshell> class A implements BooleanCondition<Object> {
...> public boolean test(Object o) {
...> return false;
...> }
...> }
jshell> intList.filter(new A());
jshell>
You can test your code by running the Test4.java
provided. Make sure your code follows the CS2030S Java style.
$ javac Test4.java
$ java Test4
$ java -jar ~cs2030s/bin/checkstyle.jar -c ~cs2030s/bin/cs2030_checks.xml *.java
Implement the map
method which takes in a parameter of type Transformer
and returns a new SourceList
where all elements of the new SourceList
are the result of applying the Transformer
to all the elements of the original SourceList
. Note: that this should create new pairs and not change the current SourceList
.
Consider the following examples:
jshell> SourceList<Integer> intList = new Pair<>(1, new Pair<>(2, new Pair<>(3, new Pair<>(4, new EmptyList<>()))))
jshell> intList.map(new IntegerToString())
$.. ==> "1", "2", "3", "4", EmptyList
jshell> intList
intList ==> 1, 2, 3, 4, EmptyList
sh -t [email protected] ssh [email protected]> SourceList<Integer> intList = new Pair<>(1, new Pair<>(2, new Pair<>(3, new Pair<>(4, new EmptyList<>()))))
jshell> SourceList<String> l = intList.map(new IntegerToString())
jshell> SourceList<Integer> l = intList.map(new IntegerToString())
| Error:
| incompatible types: inference variable U has incompatible bounds
| equality constraints: java.lang.Integer
| lower bounds: java.lang.String
| SourceList<Integer> l = intList.map(new IntegerToString());
| ^--------------------------------^
jshell> new EmptyList<Integer>().map(new IntegerToString())
$.. ==> EmptyList
jshell> SourceList<String> l = new EmptyList<Integer>().map(new IntegerToString())
jshell> SourceList<Integer> l = new EmptyList<Integer>().map(new IntegerToString())
| Error:
| incompatible types: inference variable U has incompatible bounds
| equality constraints: java.lang.Integer
| lower bounds: java.lang.String
| SourceList<Integer> l = new EmptyList<Integer>().map(new IntegerToString());
| ^-------------------------------------------------^
Make sure your map
method is as flexible as you can make it.
jshell> class A implements Transformer<Object,Integer> {
...> public Integer transform(Object o) {
...> return 0;
...> }
...> }
jshell> SourceList<Object> l = strList.map(new A());
jshell>
This class should implement the Transformer
interface. The method of the class should take in a String
and return an Integer
which is the length of the String
.
Hint: The java.lang.String ``length()
method may come in handy.
Consider the following example:
jshell> SourceList<String> strList = new Pair<>("AA", new Pair<>("A", new Pair<>("", new EmptyList<>())));
jshell> strList.map(new StringToLength())
$.. ==> 2, 1, 0, EmptyList
jshell> SourceList<Integer> l = strList.map(new StringToLength())
jshell> SourceList<String> l = strList.map(new StringToLength())
| Error:
| incompatible types: inference variable U has incompatible bounds
| equality constraints: java.lang.String
| lower bounds: java.lang.Integer
| SourceList<String> l = strList.map(new StringToLength());
| ^-------------------------------^
jshell> SourceList<Integer> intList = new Pair<>(1, new Pair<>(2, new Pair<>(3, new Pair<>(4, new EmptyList<>()))))
jshell> intList.map(new IntegerToString()).map(new StringToLength())
$.. ==> 3, 3, 3, 3, EmptyList
jshell> new EmptyList<Integer>().map(new IntegerToString()).map(new StringToLength())
$.. ==> EmptyList
jshell> strList.map(new StringToLength()).filter(new GreaterThanTwo())
$.. ==> EmptyList
jshell> intList.filter(new GreaterThanTwo()).map(new IntegerToString())
$.. ==> "3", "4", EmptyList
You can test your code by running the Test5.java
provided. Make sure your code follows the CS2030S Java style.
$ javac Test5.java
$ java Test5
$ java -jar ~cs2030s/bin/checkstyle.jar -c ~cs2030s/bin/cs2030_checks.xml *.java
END OF PAPER