Beginner Java Exercise with Data Types
Below is a data type that represents a list that has a maximum length of one. It is often useful in cases where null would otherwise be used.
Consider for example, the getHeaders method on HttpServletRequest which returns a String but might return null if there is no such header. Instead, a new API might return NoneOrOne<String> and do away with the use of null.
The idea of this exercise is to fill out the method bodies (remove the thrown Error) according to the comments without altering the method type. It is not permitted to use null or throw any exception. The tests at the bottom (see main method) should produce the specified results. At the moment, the code compiles, but will not execute successfully.
There are nine methods that need filling out. The tests are not exhaustive (use some intuition). Have fun! Questions are welcome.
// A list that is either empty or has one element. public abstract class NoneOrOne<A> { // The key abstract method (catamorphism). public abstract <X> X fold(Thunk<X> none, Func<A, X> one); // Produces an empty list. public static <A> NoneOrOne<A> none() { throw new Error("todo"); } // Produces a list with the given element. public static <A> NoneOrOne<A> one(final A a) { throw new Error("todo"); } // Returns true if this list is empty. public boolean isEmpty() { throw new Error("todo"); } // Maps the given function on each element of this list. public <B> NoneOrOne<B> map(final Func<A, B> f) { throw new Error("todo"); } // Filters the list on the given predicate. // The element is retained if the predicate satisfies. public NoneOrOne<A> filter(final Func<A, Boolean> p) { throw new Error("todo"); } // Applies the possible function on this list. public <B> NoneOrOne<B> app(final NoneOrOne<Func<A, B>> f) { throw new Error("todo"); } // Binds the given function on this list. public <B> NoneOrOne<B> bind(final Func<A, NoneOrOne<B>> f) { throw new Error("todo"); } // Returns the value held in this list. // However, if it is empty, return the given default value. public A get(final Thunk<A> def) { throw new Error("todo"); } // If this list is empty, return the given one. // Otherwise, return this list. public NoneOrOne<A> orElse(final NoneOrOne<A> els) { throw new Error("todo"); } // For debugging public String toString() { final StringBuilder s = new StringBuilder(); s.append('['); fold(new Thunk<Object>() { public Object value() { return null; // Java has no suitable unit type. } }, new Func<A, Object>() { public Object apply(final A a) { s.append(a); return null; // Java has no suitable unit type. } }); return s.append(']').toString(); } // TEST public static void main(final String[] args) { final NoneOrOne<Integer> s = NoneOrOne.one(Integer.parseInt(args[0])); final NoneOrOne<String> t = s.map(new Func<Integer, String>() { public String apply(final Integer i) { return new StringBuilder(Integer.valueOf(i * 123).toString()).reverse().toString(); } }); final NoneOrOne<Integer> u = s.filter(new Func<Integer, Boolean>() { public Boolean apply(final Integer i) { return i < 100; } }); final NoneOrOne<String> v = s.app(NoneOrOne.<Func<Integer, String>>one( new Func<Integer, String>() { public String apply(final Integer i) { return String.valueOf(Math.pow(i, i)); } })); final NoneOrOne<String> w = s.bind(new Func<Integer, NoneOrOne<String>>() { public NoneOrOne<String> apply(final Integer i) { return i % 2 == 0 ? one("it's even") : i % 3 == 0 ? one("it's divisible by 3 but not 6") : NoneOrOne.<String>none(); } }); final Integer x = s.get(new Thunk<Integer>() { public Integer value() { return 42; } }); final NoneOrOne<Integer> y = NoneOrOne.<Integer>none().orElse(s); /* $ java NoneOrOne 122 [122] [60051] [] [3.4347832971354663E254] [it's even] 122 [122] $ java -classpath /tmp/ NoneOrOne 12 [12] [6741] [12] [8.916100448256E12] [it's even] 12 [12] */ System.out.println(s); System.out.println(t); System.out.println(u); System.out.println(v); System.out.println(w); System.out.println(x); System.out.println(y); } } // Laziness interface Thunk<T> { T value(); } // Takes an A and produces a B interface Func<A, B> { B apply(A a); }
May 12th, 2010 at 6:01 pm
Thanks! Enjoyed solving this.
Two questions:
1/ What is the function of the type in the definition of the filter method?
2/ In the example output on 122, why should it not output ‘[it's even]‘?
May 13th, 2010 at 9:13 am
Hi Misha,
1/ I’m not sure I understand this question. The filter function takes a predicate (A -> Boolean) and if the list is non-empty, ensures that the element satisfies this predicate. In any other case, an empty list is returned.
2/ Woops, yes. Corrected, thanks.
May 13th, 2010 at 9:39 am
1/ Perhaps there is confusion caused by the superfluous <B> type declaration in the filter method?
May 13th, 2010 at 10:07 am
Well woopsy doopsy indeed. Thanks Kristian.
June 21st, 2010 at 12:45 am
Don’t get wat u guys r trying to do here I am a rookie in java cn u guyz break it down
September 18th, 2010 at 12:41 am
Hi @all,
first of all I would like to thank you for this great exercise. I implemented all the methods and wrote same additional tests using TestNG which all works fine.
1/ What should returned if in method app none() is supplied as parameter?
2/ Is there a provided solution to this or can you just send it to me via Email?
Greetings
Andreas
September 18th, 2010 at 2:21 am
While I was a bit playing with the code I determined another optimization for the toString() method which in my opinion should not just be for debugging
Here it is:
@Override
public String toString() {
final StringBuilder s = new StringBuilder();
s.append("[");
if (!isEmpty()) {
s.append(get(defaultThrowingThunk).toString());
}
return s.append("]“).toString();
}
whereby
defaultThrowingThunkis just aThunkobject which throws an exception within methodevalue().Greetings
Andreas
P.S.: what is the correct markup value for <code> tag for java code highlighting?
June 1st, 2011 at 8:36 am
when I run this program by a eclipse it does not work properly. that means it throws the error.
June 1st, 2011 at 9:01 am
Works fine for me dude.