forked from functionaljava/functionaljava
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNonEmptyList.java
More file actions
407 lines (358 loc) · 12.7 KB
/
NonEmptyList.java
File metadata and controls
407 lines (358 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
package fj.data;
import fj.*;
import fj.function.Effect1;
import java.util.Collection;
import java.util.Iterator;
import static fj.Function.flip;
import static fj.Function.identity;
import static fj.Function.uncurryF2;
import static fj.data.Option.some;
import static fj.data.Option.somes;
/**
* Provides an in-memory, immutable, singly linked list with total <code>head</code> and <code>tail</code>.
*
* @version %build.number%
*/
public final class NonEmptyList<A> implements Iterable<A> {
/**
* Returns an iterator for this non-empty list. This method exists to permit the use in a <code>for</code>-each loop.
*
* @return A iterator for this non-empty list.
*/
public Iterator<A> iterator() {
return toCollection().iterator();
}
private final A head;
private final List<A> tail;
/**
* The first element of this linked list.
*/
public A head() { return head; }
/**
* This list without the first element.
*/
public List<A> tail() { return tail; }
private NonEmptyList(final A head, final List<A> tail) {
this.head = head;
this.tail = tail;
}
/**
* Prepend the given value to this list.
*
* @param a The value to prepend.
* @return A non-empty list with an extra element.
*/
public NonEmptyList<A> cons(final A a) {
return nel(a, tail.cons(head));
}
/**
* Appends (snoc) the given element to this non empty list to produce a new non empty list. O(n).
*
* @param a The element to append to this non empty list.
* @return A new non empty list with the given element appended.
*/
public NonEmptyList<A> snoc(final A a) {
return nel(head, tail.snoc(a));
}
/**
* The length of this list.
*
* @return The length of this list.
*/
public int length() { return 1 + tail.length(); }
/**
* Appends the given list to this list.
*
* @param as The list to append.
* @return A new list with the given list appended.
*/
public NonEmptyList<A> append(final List<A> as) {
return nel(head, tail.append(as));
}
/**
* Appends the given list to this list.
*
* @param as The list to append.
* @return A new list with the given list appended.
*/
public NonEmptyList<A> append(final NonEmptyList<A> as) {
final List.Buffer<A> b = new List.Buffer<>();
b.append(tail);
b.snoc(as.head);
b.append(as.tail);
final List<A> bb = b.toList();
return nel(head, bb);
}
/**
* Performs a right-fold reduction across this list. This function uses O(length) stack space.
*/
public final A foldRight1(final F<A, F<A, A>> f) {
return reverse().foldLeft1(flip(f));
}
/**
* Performs a right-fold reduction across this list. This function uses O(length) stack space.
*/
public final A foldRight1(final F2<A, A, A> f) {
return reverse().foldLeft1(flip(f));
}
/**
* Performs a left-fold reduction across this list. This function runs in constant space.
*/
public final A foldLeft1(final F<A, F<A, A>> f) {
return foldLeft1(uncurryF2(f));
}
/**
* Performs a left-fold reduction across this list. This function runs in constant space.
*/
public final A foldLeft1(final F2<A, A, A> f) {
A x = head;
for (List<A> xs = tail; !xs.isEmpty(); xs = xs.tail()) {
x = f.f(x, xs.head());
}
return x;
}
/**
* Maps the given function across this list.
*
* @param f The function to map across this list.
* @return A new list after the given function has been applied to each element.
*/
public <B> NonEmptyList<B> map(final F<A, B> f) {
return nel(f.f(head), tail.map(f));
}
/**
* Binds the given function across each element of this list with a final join.
*
* @param f The function to apply to each element of this list.
* @return A new list after performing the map, then final join.
*/
public <B> NonEmptyList<B> bind(final F<A, NonEmptyList<B>> f) {
final List.Buffer<B> b = new List.Buffer<>();
final NonEmptyList<B> p = f.f(head);
b.snoc(p.head);
b.append(p.tail);
tail.foreachDoEffect(a -> {
final NonEmptyList<B> p1 = f.f(a);
b.snoc(p1.head);
b.append(p1.tail);
});
final List<B> bb = b.toList();
return nel(bb.head(), bb.tail());
}
/**
* Returns a NonEmptyList of the sublists of this list.
*
* @return a NonEmptyList of the sublists of this list.
*/
public NonEmptyList<NonEmptyList<A>> sublists() {
return fromList(
somes(toList().toStream().substreams()
.map(F1Functions.o(NonEmptyList::fromList, Conversions.Stream_List())).toList())).some();
}
/**
* Returns a NonEmptyList of the tails of this list. A list is considered a tail of itself for the purpose of this
* function (Comonad pattern).
*
* @return A NonEmptyList of the tails of this list.
*/
public NonEmptyList<NonEmptyList<A>> tails() {
return fromList(somes(toList().tails().map(NonEmptyList::fromList))).some();
}
/**
* Maps the given function across the tails of this list (comonad pattern).
*
* @param f The function to map across the tails of this list.
* @return The results of applying the given function to the tails of this list, as a NonEmptyList.
*/
public <B> NonEmptyList<B> mapTails(final F<NonEmptyList<A>, B> f) {
return tails().map(f);
}
/**
* Intersperses the given argument between each element of this non empty list.
*
* @param a The separator to intersperse in this non empty list.
* @return A non empty list with the given separator interspersed.
*/
public NonEmptyList<A> intersperse(final A a) {
final List<A> list = toList().intersperse(a);
return nel(list.head(), list.tail());
}
/**
* Reverse this non empty list in constant stack space.
*
* @return A new non empty list with the elements in reverse order.
*/
public NonEmptyList<A> reverse() {
final List<A> list = toList().reverse();
return nel(list.head(), list.tail());
}
/**
* Sorts this non empty list using the given order over elements using a <em>merge sort</em> algorithm.
*
* @param o The order over the elements of this non empty list.
* @return A sorted non empty list according to the given order.
*/
public NonEmptyList<A> sort(final Ord<A> o) {
final List<A> list = toList().sort(o);
return nel(list.head(), list.tail());
}
/**
* Returns the minimum element in this non empty list according to the given ordering.
*
* @param o An ordering for the elements of this non empty list.
* @return The minimum element in this list according to the given ordering.
*/
public final A minimum(final Ord<A> o) {
return foldLeft1(o::min);
}
/**
* Returns the maximum element in this non empty list according to the given ordering.
*
* @param o An ordering for the elements of this non empty list.
* @return The maximum element in this list according to the given ordering.
*/
public final A maximum(final Ord<A> o) {
return foldLeft1(o::max);
}
/**
* Zips this non empty list with the given non empty list to produce a list of pairs. If this list and the given list
* have different lengths, then the longer list is normalised so this function never fails.
*
* @param bs The non empty list to zip this non empty list with.
* @return A new non empty list with a length the same as the shortest of this list and the given list.
*/
public <B> NonEmptyList<P2<A, B>> zip(final NonEmptyList<B> bs) {
final List<P2<A, B>> list = toList().zip(bs.toList());
return nel(list.head(), list.tail());
}
/**
* Zips this non empty list with the index of its element as a pair.
*
* @return A new non empty list with the same length as this list.
*/
public NonEmptyList<P2<A, Integer>> zipIndex() {
final List<P2<A, Integer>> list = toList().zipIndex();
return nel(list.head(), list.tail());
}
/**
* Zips this non empty list with the given non empty list using the given function to produce a new list. If this list
* and the given list have different lengths, then the longer list is normalised so this function
* never fails.
*
* @param bs The non empty list to zip this non empty list with.
* @param f The function to zip this non empty list and the given non empty list with.
* @return A new non empty list with a length the same as the shortest of this list and the given list.
*/
public <B, C> NonEmptyList<C> zipWith(final List<B> bs, final F<A, F<B, C>> f) {
final List<C> list = toList().zipWith(bs, f);
return nel(list.head(), list.tail());
}
/**
* Zips this non empty list with the given non empty list using the given function to produce a new list. If this list
* and the given list have different lengths, then the longer list is normalised so this function
* never fails.
*
* @param bs The non empty list to zip this non empty list with.
* @param f The function to zip this non empty list and the given non empty list with.
* @return A new non empty list with a length the same as the shortest of this list and the given list.
*/
public <B, C> NonEmptyList<C> zipWith(final List<B> bs, final F2<A, B, C> f) {
final List<C> list = toList().zipWith(bs, f);
return nel(list.head(), list.tail());
}
/**
* Transforms a non empty list of pairs into a non empty list of first components and
* a non empty list of second components.
*
* @param xs The non empty list of pairs to transform.
* @return A non empty list of first components and a non empty list of second components.
*/
public static <A, B> P2<NonEmptyList<A>, NonEmptyList<B>> unzip(final NonEmptyList<P2<A, B>> xs) {
final P2<List<A>, List<B>> p = List.unzip(xs.toList());
return P.p(nel(p._1().head(), p._1().tail()), nel(p._2().head(), p._2().tail()));
}
/**
* Returns a <code>List</code> projection of this list.
*
* @return A <code>List</code> projection of this list.
*/
public List<A> toList() {
return tail.cons(head);
}
/**
* Projects an immutable collection of this non-empty list.
*
* @return An immutable collection of this non-empty list.
*/
public Collection<A> toCollection() {
return toList().toCollection();
}
/**
* Returns a function that takes a non-empty list to a list.
*
* @return A function that takes a non-empty list to a list.
*/
public static <A> F<NonEmptyList<A>, List<A>> toList_() {
return NonEmptyList::toList;
}
/**
* Return a non-empty list with the given head and tail.
*
* @param head The first element of the new list.
* @param tail The remaining elements of the new list.
* @return A non-empty list with the given head and tail.
*/
public static <A> NonEmptyList<A> nel(final A head, final List<A> tail) {
return new NonEmptyList<>(head, tail);
}
/**
* Constructs a non empty list from the given elements.
*
* @param head The first in the non-empty list.
* @param tail The elements to construct a list's tail with.
* @return A non-empty list with the given elements.
*/
@SafeVarargs public static <A> NonEmptyList<A> nel(final A head, final A... tail) {
return nel(head, List.list(tail));
}
/**
* Returns a function that puts an element into a non-empty list.
*
* @return A function that puts an element into a non-empty list.
*/
public static <A> F<A, NonEmptyList<A>> nel() {
return a -> nel(a);
}
/**
* Returns a potential non-empty list from the given list. A non-value is returned if the given list is empty.
*
* @param as The list to construct a potential non-empty list with.
* @return A potential non-empty list from the given list.
*/
public static <A> Option<NonEmptyList<A>> fromList(final List<A> as) {
return as.isEmpty() ?
Option.none() :
some(nel(as.head(), as.tail()));
}
/**
* Concatenate (join) a non empty list of non empty lists.
*
* @param o The non empty list of non empty lists to join.
* @return A new non empty list that is the concatenation of the given lists.
*/
public static <A> NonEmptyList<A> join(final NonEmptyList<NonEmptyList<A>> o) { return o.bind(identity()); }
/**
* Perform an equality test on this list which delegates to the .equals() method of the member instances.
* This is implemented with Equal.nonEmptyListEqual using the anyEqual rule.
*
* @param obj the other object to check for equality against.
* @return true if this list is equal to the provided argument
*/
@Override public boolean equals( final Object obj ) {
return Equal.equals0(NonEmptyList.class, this, obj, () -> Equal.nonEmptyListEqual(Equal.anyEqual()));
}
@Override public int hashCode() {
return Hash.nonEmptyListHash(Hash.<A>anyHash()).hash(this);
}
@Override public String toString() { return Show.nonEmptyListShow(Show.<A>anyShow()).showS(this); }
}