For my assignment we are to make an emulation of a LISP Linked List in Java. There are two types of List, an EmptyList and a NonEmptyList. The EmptyList is mostly trivial and only serves the purpose of ending the Linked List. The way it is supposed to work is that each List has a head and a tail. The head is an Object and the Tail is the next Linked List. I have a Linked List interface as follows:
public interface LispList {
EmptyList NIL = new EmptyList();
boolean empty();
Object head();
LispList tail();
LispList cons(Object inputHead);
}
And here is the NonEmptyList class:
public class NonEmptyList implements LispList{
Object head;
LispList tail;
public NonEmptyList(Object inputHead) {
this.head = inputHead;
this.tail = new NonEmptyList(head);
}
public boolean empty() {
return false;
}
public Object head() {
return head;
}
public LispList tail() {
return tail;
}
public String toString() {
return head() + " " + tail().toString();
}
public NonEmptyList cons(Object inputHead) {
NonEmptyList a = new NonEmptyList(inputHead);
return a;
}
public class NIL{
EmptyList NIL;
}
}
EmptyList:
public class EmptyList implements LispList {
public EmptyList() {
}
public boolean empty() {
return true;
}
public Object head() {
throw new UnsupportedOperationException("EmptyList");
}
public LispList tail() {
throw new UnsupportedOperationException("EmptyList");
}
public String toString() {
return "";
}
public class NIL{
EmptyList NIL;
}
public NonEmptyList cons(Object inputHead) {
NonEmptyList a = new NonEmptyList(inputHead);
return a;
}
}
And here is my tester:
public class LispListTest {
public static void main(String[] args) {
LispList list = LispList.NIL.cons("C").cons("B").cons("A");
System.out.println(list.tail());
System.out.println(list.toString());
}
}
The problem I am having is in the constructor of the NonEmptyList. The way I have it currently gives me a Stack Overflow Exception. I have tried a few different things and none of them work the way I need them to. I'm not sure how to make the constructor so the tail points to the next list.
This is my first attempt at a linked list so I might be making a pretty simple mistake.
First, EmptyList
class needs to be singleton. Not sure how to achieve it Java, but you shouldn't open the constructor to everybody to use.
Then, the constructor for NonEmptyList
should take two arguments:
LispList
. Better if you overload the constructor with this argument defaulting to EmptyList
(singleton) instance. Currently, in the constructor NonEmptyList
you recursively call it when assigning the tail
: in a way you are constructing an infinite list with repeated element: (a) this is not what you want and (b) without laziness this will cause stack overflow.
Lastly, cons
is a constructor for a non-empty list, thus there is no need for a method called cons
.
Most Lisp dialects construct the list on the top of a pair . There is some criticism about the way Lisps do it: this introduces concepts of proper and improper lists and it's hard to define a generic comparison on lists. Yet, it's an easy and efficient way to do it.
A pair is constructed using CONS
function (I will be using Common Lisp, CL, for demonstration):
(cons 12 45) => (12 . 45)
Notice the dot in the printed form of the pair. Parts of the pair can be extracted using functions CAR
and CDR
:
(car (cons 12 45)) => 12
(cdr (cons 12 45)) => 45
Pairs can be combined with other pairs:
(cons (cons (cons 1 2) 3) (cons 4 5))
=> (((1 . 2) . 3) 4 . 5)
CL provides combination functions of CAR
and CDR
to extract sub-pairs, eg CDDAR
is a shortcut for (CDR (CDR (CAR OBJ)))
: it takes the first item of the pair (which must be a pair itself), then the second item of the result and the second item of that result.
Lisps also define a special object of an empty pair, or nothing (but in fact, this object is not a pair, it's like mathematical "empty set" which is not a set...). In CL there two synonyms for it: NIL
or ()
.
A list is constructed using pairs ordered in certain way. A list is:
NIL
, or empty (CONS OBJ TAIL)
, where OBJ
is any object and TAIL
is a list. To distinguish operations on pairs and their combinations from operations on lists, Common Lisp provides synonymous functions:
FIRST
extracts the first item of the list (aka head), synonym for CAR
. REST
returns the tail of the list, synonym for (CAR (CDR LIST))
or (CADR LIST)
. So, here are examples of lists:
NIL
is an empty list (CONS 1 NIL)
(printed (1)
) is the list of one element; FIRST
will return 1 and REST
will return an empty list NIL
. (CONS 1 (CONS 2 NIL))
(printed (1 2)
) is the list of two elements; FIRST
will return 1 and REST
will return the tail list (2)
. N
will be (CONS 1 (CONS 2 (CONS 3 ... (CONS N NIL) ..)))
. Here is correct and tested answer:
LispList interface:
public interface LispList
{
LispList NIL = new EmptyList();
boolean isEmpty();
Object head();
LispList tail();
LispList cons(Object head);
}
EmptyList class
public class EmptyList implements LispList {
public String toString() {
return "";
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Object head() {
throw new UnsupportedOperationException();
}
@Override
public LispList tail() {
throw new UnsupportedOperationException();
}
@Override
public LispList cons(Object head) {
return new NonEmptyList(head, new EmptyList());
}
}
NonEmptyList class
public class NonEmptyList implements LispList {
private LispList tail;
private Object head;
public NonEmptyList(Object head, LispList tail) {
this.head = head;
this.tail = tail;
}
public String toString() {
return head() + " " + tail().toString();
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public Object head() {
return head;
}
@Override
public LispList tail() {
return tail;
}
@Override
public LispList cons(Object head) {
return new NonEmptyList(head, new NonEmptyList(head(), tail));
}
}
And test main class:
public class LispListTester
{
public static void main(String[] args)
{
LispList list1 = new EmptyList();
System.out.println("[" + list1 + "]");
System.out.println("Expected: []");
LispList list2 = new NonEmptyList("A", new EmptyList());
System.out.println(list2);
System.out.println("Expected: A");
LispList list3 = new NonEmptyList("A", new NonEmptyList("B",
new NonEmptyList("C", new EmptyList())));
System.out.println(list3);
System.out.println("Expected: A B C");
LispList list4 = LispList.NIL.cons("E").cons("D").cons("C").cons("B").cons("A");
System.out.println(list4);
System.out.println("Expected: A B C D E");
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.