ML / (Strict) Haskell in Java
Dies ist aus einem tatsächlichen realen Projekt. Es nutzt persistente unveränderliche Datenstrukturen und verwendet eine Rekursion, auch wenn dies nicht erforderlich ist. Eigentlich ist es eher wie Kore (die Sprache, die das Projekt implementiert) in Java, aber der Stil ist im Grunde der gleiche wie in ML. Die Philosophie von Kore ist jedoch, dass der Autor seinen Code nicht formatieren sollte. Daher wird auch kein Java-Code formatiert (er wird von Eclipse automatisch formatiert).
lösche n Elemente aus einer Liste :
public static <T> List<T> drop(List<T> l, Integer n) {
return n == 0 ? l : drop(l.cons().tail, n - 1);
}
In ML / Haskell, wo Sie das Muster abgleichen würden, um Kopf und Schwanz zu extrahieren, sagen Sie list.cons().x
und list.cons().tail
.
Ein Element in eine Liste einfügen :
public static <T> List<T> insert(List<T> l, Integer i, T x) {
if (i == 0)
return cons(x, l);
return cons(l.cons().x, insert(l.cons().tail, i - 1, x));
}
Liste ist wörtlich definiert, wie der algebraische Datentyp definiert werden würde. Hier ist eine Version, bei der die von Eclipse generierte Kesselplatte entfernt wurde:
public final class List<T> {
public static final class Nil<T> {
}
public static final class Cons<T> {
public final T x;
public final List<T> tail;
public Cons(T x, List<T> tail) {
if (x == null)
throw new RuntimeException("null head");
if (tail == null)
throw new RuntimeException("null tail");
this.x = x;
this.tail = tail;
}
}
private final Nil<T> nil;
private final Cons<T> cons;
private List(Nil<T> nil, Cons<T> cons) {
this.nil = nil;
this.cons = cons;
}
public boolean isEmpty() {
return nil != null;
}
public Nil<T> nil() {
if (nil == null)
throw new RuntimeException("not nil");
return nil;
}
public Cons<T> cons() {
if (cons == null)
throw new RuntimeException("not cons");
return cons;
}
public static <T> List<T> cons(Cons<T> cons) {
if (cons == null)
throw new RuntimeException("constructor received null");
return new List<T>(null, cons);
}
public static <T> List<T> nil(Nil<T> nil) {
if (nil == null)
throw new RuntimeException("constructor received null");
return new List<T>(nil, null);
}
}
Hier ist eine Kartendatenstruktur, die in Form eines Versuchs implementiert ist :
public final class Map<K, V> {
private final Tree<Character, Optional<Pair<K, V>>> tree;
// keys are sorted in reverse order so entrySet can use cons instead of append
private final Comparer<Pair<Character, Tree<Character, Optional<Pair<K, V>>>>> comparer =
new PairLeftComparer<Character, Tree<Character, Optional<Pair<K, V>>>>(
new ReverseComparer<Character>(new CharacterComparer()));
private Map(Tree<Character, Optional<Pair<K, V>>> tree) {
this.tree = tree;
}
public static <K, V> Map<K, V> empty() {
return new Map<K, V>(new Tree<Character, Optional<Pair<K, V>>>(
OptionalUtils.<Pair<K, V>> nothing(),
ListUtils
.<Pair<Character, Tree<Character, Optional<Pair<K, V>>>>> nil()));
}
public Optional<V> get(K k) {
Tree<Character, Optional<Pair<K, V>>> t = tree;
for (char c : k.toString().toCharArray()) {
Tree<Character, Optional<Pair<K, V>>> t2 = getEdge(t, c);
if (t2 == null)
return nothing();
t = t2;
}
if (t.v.isNothing())
return nothing();
return some(t.v.some().x.y);
}
public Map<K, V> put(K k, V v) {
return new Map<K, V>(put(tree, k.toString(), v, k));
}
private Tree<Character, Optional<Pair<K, V>>> put(
Tree<Character, Optional<Pair<K, V>>> t, String s, V v, K k) {
if (s.equals(""))
return new Tree<Character, Optional<Pair<K, V>>>(some(Pair.pair(k, v)),
t.edges);
char c = s.charAt(0);
Tree<Character, Optional<Pair<K, V>>> t2 = getEdge(t, c);
if (t2 == null)
return new Tree<Character, Optional<Pair<K, V>>>(
t.v,
sort(
cons(
pair(
c,
put(new Tree<Character, Optional<Pair<K, V>>>(
OptionalUtils.<Pair<K, V>> nothing(),
ListUtils
.<Pair<Character, Tree<Character, Optional<Pair<K, V>>>>> nil()),
s.substring(1), v, k)), t.edges), comparer));
return new Tree<Character, Optional<Pair<K, V>>>(t.v, sort(
replace(pair(c, put(t2, s.substring(1), v, k)), t.edges), comparer));
}
private List<Pair<Character, Tree<Character, Optional<Pair<K, V>>>>> replace(
Pair<Character, Tree<Character, Optional<Pair<K, V>>>> edge,
List<Pair<Character, Tree<Character, Optional<Pair<K, V>>>>> edges) {
if (edges.cons().x.x.equals(edge.x))
return cons(edge, edges.cons().tail);
return cons(edges.cons().x, replace(edge, edges.cons().tail));
}
// I consider this O(1). There are a constant of 2^16 values of
// char. Either way it's unusual to have a large amount of
// edges since only ASCII chars are typically used.
private Tree<Character, Optional<Pair<K, V>>> getEdge(
Tree<Character, Optional<Pair<K, V>>> t, char c) {
for (Pair<Character, Tree<Character, Optional<Pair<K, V>>>> p : iter(t.edges))
if (p.x.equals(c))
return p.y;
return null;
}
public Map<K, V> delete(K k) {
return new Map<K, V>(delete(tree, k.toString()).x);
}
private Pair<Tree<Character, Optional<Pair<K, V>>>, Boolean> delete(
Tree<Character, Optional<Pair<K, V>>> t, String k) {
if (k.equals(""))
return pair(
new Tree<Character, Optional<Pair<K, V>>>(
OptionalUtils.<Pair<K, V>> nothing(), t.edges), t.edges.isEmpty());
char c = k.charAt(0);
Tree<Character, Optional<Pair<K, V>>> t2 = getEdge(t, c);
if (t2 == null)
return pair(t, false);
Pair<Tree<Character, Optional<Pair<K, V>>>, Boolean> p =
delete(t2, k.substring(1));
List<Pair<Character, Tree<Character, Optional<Pair<K, V>>>>> edges = nil();
for (Pair<Character, Tree<Character, Optional<Pair<K, V>>>> e : iter(t.edges))
if (!e.x.equals(c))
edges = cons(e, edges);
if (!p.y)
return pair(
new Tree<Character, Optional<Pair<K, V>>>(t.v, cons(pair(c, p.x),
edges)), false);
boolean oneEdge = t.edges.cons().tail.isEmpty();
return pair(new Tree<Character, Optional<Pair<K, V>>>(t.v, edges), oneEdge
&& t.v.isNothing());
}
public static class Entry<K, V> {
public Entry(K k, V v) {
this.k = k;
this.v = v;
}
public final K k;
public final V v;
}
public List<Entry<K, V>> entrySet() {
return entrySet(ListUtils.<Entry<K, V>> nil(), tree);
}
private List<Entry<K, V>> entrySet(List<Entry<K, V>> l,
Tree<Character, Optional<Pair<K, V>>> t) {
if (!t.v.isNothing()) {
Pair<K, V> p = t.v.some().x;
l = cons(new Entry<K, V>(p.x, p.y), l);
}
for (Pair<Character, Tree<Character, Optional<Pair<K, V>>>> e : iter(t.edges))
l = entrySet(l, e.y);
return l;
}
}
Die Typen fangen an, so viel Platz wie der Code einzunehmen. In put hat die Methode beispielsweise 302 Zeichen und 343 Zeichen Code (ohne Leerzeichen / Zeilenumbrüche).
.litcoffee
. Es könnte helfen.