home—lectures—exams—hws—breeze (snow day)
lect09a
Trees in Java, cont.
Finish: writing List<String> AncTree.allNames().
Write entitle, in Java,
having seen the racket solution.
Aside: the meaning(s) of null
Btw, compare this to the traditional approach to trees in CS2:
in our approach
we don't actually mention null,
and we won't have any NullPointerExceptions.
In traditional binary trees, null is used
to represent data -- it represents the empty tree(Node).
BUT: you can't call any methods on null
(even though it makes sense to call methods on an empty tree).
Even worse, the traditional type declaration makes it look like
class Node {
String name;
Node left; // well, a real Node *or* null
Node right; // well, a real Node *or* null
}
|
We now are conflating Nodes (which contain data)
with empty trees (which don't), and the fact that
there is a supertype for both empty and non-empty trees.
In our approach we just recurred on this.ma
and this.pa.
But here, we have to be checking for null
before calling any (recursive) method.
This is not only
repeated code (ugly/redundant),
it's also
easy for programmers to forget.
The problem stems from the fact that the recursive data definition
has three types
(Node, emptyTree, and Tree),
but the traditional approach program uses one-or-two
(Node, null, and
…places where we
need to check for null-vs.-Node).
Without getting the data-definition correct (the one mathematicians
use for lists and trees),
we suddenly are missing a methodology,
and our code no longer can mirror the data.
Note that null certainly does have its uses
as a sentinel:
for example, a search function might return the object found
or null.
(In racket, false is the idiomatic sentinel.)
Since null type-checks as any type,
we can still say our function returns a Node
to appease the type-checker;
in comments we say "this function returns a Node
or null".
The important thing to remember is that anybody who calls
the function needs to treat the result as a union type,
and do a cond or dispatch on it.
In Java, the type system fails to remind us of this,
and programmers easily forget.
Haskell has an interesting approach:
They use the composite pattern to represent the union of null-or-something.
In java-ish terms:
// A non-helpful Java implementation of Haskell's Maybe/Just/Nothing types:
abstract class Maybe<T> {}
<T> class Nothing extends Maybe<T> {} // The "null" case.
class Just<T> extends Maybe<T> {
T val;
Maybe(T _val) { this.val = val; }
}
|
In java, this doesn't help us much because the type system gets in our way.
However, in Haskell's type system, the type system makes requires that your code
handle the Nothing (null) case, if a function returns that value.
Btw:
Objects.html
Note that this class is created so that we have null-safe variants of common methods;
they are therefore necessarily static methods.
Java: obj.meth(...) [also with implicit 'this']
Class.meth(...)
a + b (+ a b)
!a (not a)
new int[23] (make-vector 23 0) or: (build-vector 23 (lambda (i) (* 3 i)))
arr[i] (vector-ref arr i) Btw, compare to List.get(i)
arr[i] = ... (vector-set! arr i ...) Btw, compare to List.set(i,...)
new Widget(...) (make-widget ...)
super(...)
this(...)
obj.f (widget-f obj)
obj.f = ... (set-widget-f! obj ...)
(String literals call string constructor?)
(lect09b:
Show grammar for N0;
give examples of expressions
*and* parse trees;
give internal-representation data def'n, and examples
Discuss what read,eval,print should do.
lect09c:
regexp code?
show in passing
http://mainisusuallyafunction.blogspot.com/2011/10/quasicrystals-as-sums-of-waves-in-plane.html
go over parsing ?
home—lectures—exams—hws—breeze (snow day)