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}