Refactor append code

This commit is contained in:
Mike Cifelli 2018-03-15 08:25:42 -04:00
parent 9d13c3d892
commit d7f908a01d
4 changed files with 69 additions and 63 deletions

View File

@ -1,6 +1,7 @@
package function.builtin.cons; package function.builtin.cons;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import static table.FunctionTable.lookupFunction;
import function.ArgumentValidator; import function.ArgumentValidator;
import function.FunctionNames; import function.FunctionNames;
@ -11,20 +12,47 @@ import sexpression.Cons;
public class APPEND extends LispFunction { public class APPEND extends LispFunction {
public static Cons append(Cons firstList, Cons secondList) { public static Cons append(Cons firstList, Cons secondList) {
Cons appendedLists = copy(firstList); return lookupAppend().appendLists(firstList, secondList);
}
private static APPEND lookupAppend() {
return (APPEND) lookupFunction("APPEND");
}
private ArgumentValidator argumentValidator;
private ArgumentValidator firstListValidator;
public APPEND(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2);
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
this.firstListValidator = new ArgumentValidator(name + "|first-list|");
}
@Override
public Cons call(Cons argumentList) {
argumentValidator.validate(argumentList);
Cons rest = (Cons) argumentList.getRest();
Cons firstList = (Cons) argumentList.getFirst();
Cons secondList = (Cons) rest.getFirst();
return appendLists(firstList, secondList);
}
private Cons appendLists(Cons firstList, Cons secondList) {
firstListValidator.validate(firstList);
if (firstList.isNull()) if (firstList.isNull())
return copy(secondList); return secondList;
getLastItem(appendedLists).setRest(copy(secondList)); Cons appendedLists = copy(firstList);
getLastItem(appendedLists).setRest(secondList);
return appendedLists; return appendedLists;
} }
private static Cons copy(Cons list) { private Cons copy(Cons list) {
if (list.isNull())
return NIL;
Cons newList = new Cons(list.getFirst(), NIL); Cons newList = new Cons(list.getFirst(), NIL);
Cons builder = newList; Cons builder = newList;
@ -36,7 +64,7 @@ public class APPEND extends LispFunction {
return newList; return newList;
} }
private static Cons getLastItem(Cons list) { private Cons getLastItem(Cons list) {
Cons tail = list; Cons tail = list;
while (tail.getRest().isCons()) while (tail.getRest().isCons())
@ -44,33 +72,4 @@ public class APPEND extends LispFunction {
return tail; return tail;
} }
private ArgumentValidator argumentValidator;
private String name;
public APPEND(String name) {
this.name = name;
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2);
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
}
@Override
public Cons call(Cons argumentList) {
argumentValidator.validate(argumentList);
Cons rest = (Cons) argumentList.getRest();
Cons firstList = (Cons) argumentList.getFirst();
Cons secondList = (Cons) rest.getFirst();
validateLists(firstList, secondList);
return append(firstList, secondList);
}
private void validateLists(Cons firstList, Cons secondList) {
ArgumentValidator properListValidatorOne = new ArgumentValidator(name + "|list-one|");
ArgumentValidator properListValidatorTwo = new ArgumentValidator(name + "|list-two|");
properListValidatorOne.validate(firstList);
properListValidatorTwo.validate(secondList);
}
} }

View File

@ -3,6 +3,7 @@ package function.builtin.cons;
import static function.builtin.cons.LIST.makeList; import static function.builtin.cons.LIST.makeList;
import static recursion.TailCalls.done; import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall; import static recursion.TailCalls.tailCall;
import static table.FunctionTable.lookupFunction;
import java.math.BigInteger; import java.math.BigInteger;
@ -17,25 +18,13 @@ import sexpression.LispNumber;
public class LENGTH extends LispFunction { public class LENGTH extends LispFunction {
public static BigInteger getLength(Cons list) { public static BigInteger getLength(Cons list) {
return callWithoutArgumentValidation(makeList(list)).getValue(); LispNumber length = lookupLength().callWithoutArgumentValidation(makeList(list));
return length.getValue();
} }
private static LispNumber callWithoutArgumentValidation(Cons argumentList) { private static LENGTH lookupLength() {
return callTailRecursive(BigInteger.ZERO, argumentList).invoke(); return (LENGTH) lookupFunction("LENGTH");
}
private static TailCall<LispNumber> callTailRecursive(BigInteger accumulatedLength, Cons argumentList) {
Cons list = (Cons) argumentList.getFirst();
Cons restOfList = makeList(list.getRest());
if (list.isNull())
return done(new LispNumber(accumulatedLength));
return tailCall(() -> callTailRecursive(increment(accumulatedLength), restOfList));
}
private static BigInteger increment(BigInteger number) {
return number.add(BigInteger.ONE);
} }
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;
@ -53,4 +42,22 @@ public class LENGTH extends LispFunction {
return callTailRecursive(BigInteger.ZERO, argumentList).invoke(); return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
} }
private LispNumber callWithoutArgumentValidation(Cons argumentList) {
return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
}
private TailCall<LispNumber> callTailRecursive(BigInteger accumulatedLength, Cons argumentList) {
Cons list = (Cons) argumentList.getFirst();
Cons restOfList = makeList(list.getRest());
if (list.isNull())
return done(new LispNumber(accumulatedLength));
return tailCall(() -> callTailRecursive(increment(accumulatedLength), restOfList));
}
private BigInteger increment(BigInteger number) {
return number.add(BigInteger.ONE);
}
} }

View File

@ -1,5 +1,6 @@
package table; package table;
import static function.builtin.cons.APPEND.append;
import static function.builtin.cons.LIST.makeList; import static function.builtin.cons.LIST.makeList;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
@ -8,7 +9,6 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import function.builtin.cons.APPEND;
import sexpression.Cons; import sexpression.Cons;
import sexpression.SExpression; import sexpression.SExpression;
import sexpression.Symbol; import sexpression.Symbol;
@ -51,7 +51,7 @@ public class SymbolTable {
Cons context = NIL; Cons context = NIL;
for (Entry<String, SExpression> binding : getSortedBindings()) for (Entry<String, SExpression> binding : getSortedBindings())
context = APPEND.append(context, makeList(makeSymbolValuePair(binding))); context = append(context, makeList(makeSymbolValuePair(binding)));
return context; return context;
} }

View File

@ -43,22 +43,22 @@ public class APPENDTest extends SymbolAndFunctionCleaner {
} }
@Test @Test
public void appendMakesCopies() { public void appendMakesCopyOfFirstList() {
evaluateString("(setq x '(1 2 3))"); evaluateString("(setq x '(1 2 3))");
evaluateString("(setq y '(4 5 6))"); evaluateString("(setq y '(4 5 6))");
evaluateString("(append x y)"); evaluateString("(append x y)");
assertSExpressionsMatch(parseString("(1 2 3)"), evaluateString("x")); assertSExpressionsMatch(parseString("(1 2 3)"), evaluateString("x"));
assertSExpressionsMatch(parseString("(4 5 6)"), evaluateString("y")); }
@Test
public void appendAllowsDottedSecondList() {
String input = "(append '() (cons 3 4))";
assertSExpressionsMatch(evaluateString("(cons 3 4)"), evaluateString(input));
} }
@Test(expected = DottedArgumentListException.class) @Test(expected = DottedArgumentListException.class)
public void appendWithDottedFirstList() { public void appendWithDottedFirstList() {
evaluateString("(append '(1 2) (cons 3 4))");
}
@Test(expected = DottedArgumentListException.class)
public void appendWithDottedSecondList() {
evaluateString("(append (cons 1 2) '(3 4))"); evaluateString("(append (cons 1 2) '(3 4))");
} }