001package org.clafer.collection;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Iterator;
006import java.util.List;
007import java.util.NoSuchElementException;
008import org.clafer.common.Check;
009
010/**
011 * Persistent singly-linked list. Useful for recursive algorithms.
012 *
013 * @param <E> the type of the elements
014 * @author jimmy
015 */
016public abstract class FList<E> implements Iterable<E> {
017
018    private static Null<Object> Null = new Null<>();
019
020    private FList() {
021    }
022
023    /**
024     * Returns the first element of the list.
025     *
026     * @return the head of the list
027     */
028    public abstract E getHead();
029
030    /**
031     * Returns the a sublist of this list without the first element.
032     *
033     * @return the tail of the list
034     */
035    public abstract FList<E> getTail();
036
037    /**
038     * Checks if the list is empty.
039     *
040     * @return {@code true} if and only if the list is empty, {@code false}
041     * otherwise
042     */
043    public abstract boolean isEmpty();
044
045    /**
046     * Converts this functional list to an imperative list.
047     *
048     * @return a copy of this list
049     */
050    public abstract List<E> toList();
051
052    /**
053     * Returns an empty list. The empty list is represented by null.
054     *
055     * @param <E> the type of the elements
056     * @return an empty list
057     */
058    public static <E> FList<E> empty() {
059        @SuppressWarnings("unchecked")
060        Null<E> empty = (Null<E>) Null;
061        return empty;
062    }
063
064    /**
065     * A list containing a single element.
066     *
067     * @param <E> the type of the elements
068     * @param item the element of the list
069     * @return a list of size 1
070     */
071    public static <E> FList<E> single(E item) {
072        return cons(item, FList.<E>empty());
073    }
074
075    /**
076     * Functional-programming cons. Nondestructive.
077     *
078     * @param <E> the type of the elements
079     * @param head the beginning of the new list
080     * @param tail the end of the new list
081     * @return a copy of the original list with head appended at the start
082     */
083    public static <E> FList<E> cons(E head, FList<E> tail) {
084        return new Cons<>(head, tail);
085    }
086
087    /**
088     * Functional-programming snoc. Nondestructive.
089     *
090     * @param <E> the type of the elements
091     * @param head the beginning of the new list
092     * @param tail the end of the new list
093     * @return a copy of the original list with tail appended at the end
094     */
095    public static <E> FList<E> snoc(FList<E> head, E tail) {
096        if (head.isEmpty()) {
097            return single(tail);
098        }
099        return cons(head.getHead(), snoc(head.getTail(), tail));
100    }
101
102    private static class Cons<E> extends FList<E> {
103
104        private final E head;
105        private final FList<E> tail;
106
107        private Cons(E head, FList<E> tail) {
108            this.head = Check.notNull(head);
109            this.tail = Check.notNull(tail);
110        }
111
112        @Override
113        public E getHead() {
114            return head;
115        }
116
117        @Override
118        public FList<E> getTail() {
119            return tail;
120        }
121
122        @Override
123        public boolean isEmpty() {
124            return false;
125        }
126
127        @Override
128        public List<E> toList() {
129            List<E> list = new ArrayList<>();
130            for (FList<E> current = this; !current.isEmpty(); current = current.getTail()) {
131                list.add(current.getHead());
132            }
133            return list;
134        }
135
136        @Override
137        public Iterator<E> iterator() {
138            return new FListIterator<>(this);
139        }
140
141        @Override
142        public boolean equals(Object obj) {
143            if (obj instanceof Cons<?>) {
144                Cons<?> other = (Cons<?>) obj;
145                return head.equals(other.head) && tail.equals(other.tail);
146            }
147            return false;
148        }
149
150        @Override
151        public int hashCode() {
152            int hash = 1;
153            for (FList<E> current = this; !current.isEmpty(); current = current.getTail()) {
154                hash = 31 * hash + current.getHead().hashCode();
155            }
156            return hash;
157        }
158
159        @Override
160        public String toString() {
161            StringBuilder result = new StringBuilder();
162            result.append('[');
163            result.append(head);
164            for (FList<E> current = tail; !current.isEmpty(); current = current.getTail()) {
165                result.append(", ");
166                result.append(current.getHead());
167            }
168            return result.append(']').toString();
169        }
170    }
171
172    private static class Null<E> extends FList<E> {
173
174        @Override
175        public E getHead() {
176            throw new NoSuchElementException("No head of empty list.");
177        }
178
179        @Override
180        public FList<E> getTail() {
181            throw new NoSuchElementException("No tail of empty list.");
182        }
183
184        @Override
185        public boolean isEmpty() {
186            return true;
187        }
188
189        @Override
190        public List<E> toList() {
191            return Collections.emptyList();
192        }
193
194        @Override
195        public Iterator<E> iterator() {
196            return Collections.emptyIterator();
197        }
198
199        @Override
200        public boolean equals(Object obj) {
201            return this == obj;
202        }
203
204        @Override
205        public int hashCode() {
206            return 71;
207        }
208
209        @Override
210        public String toString() {
211            return "[]";
212        }
213    }
214
215    private static class FListIterator<E> implements Iterator<E> {
216
217        private FList<E> current;
218
219        FListIterator(FList<E> current) {
220            this.current = current;
221        }
222
223        @Override
224        public boolean hasNext() {
225            return !current.isEmpty();
226        }
227
228        @Override
229        public E next() {
230            E head = current.getHead();
231            current = current.getTail();
232            return head;
233        }
234
235        @Override
236        public void remove() {
237            throw new UnsupportedOperationException();
238        }
239    }
240}