Refactor append code
This commit is contained in:
parent
9d13c3d892
commit
d7f908a01d
|
@ -1,6 +1,7 @@
|
|||
package function.builtin.cons;
|
||||
|
||||
import static sexpression.Nil.NIL;
|
||||
import static table.FunctionTable.lookupFunction;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
|
@ -11,20 +12,47 @@ import sexpression.Cons;
|
|||
public class APPEND extends LispFunction {
|
||||
|
||||
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())
|
||||
return copy(secondList);
|
||||
return secondList;
|
||||
|
||||
getLastItem(appendedLists).setRest(copy(secondList));
|
||||
Cons appendedLists = copy(firstList);
|
||||
getLastItem(appendedLists).setRest(secondList);
|
||||
|
||||
return appendedLists;
|
||||
}
|
||||
|
||||
private static Cons copy(Cons list) {
|
||||
if (list.isNull())
|
||||
return NIL;
|
||||
|
||||
private Cons copy(Cons list) {
|
||||
Cons newList = new Cons(list.getFirst(), NIL);
|
||||
Cons builder = newList;
|
||||
|
||||
|
@ -36,7 +64,7 @@ public class APPEND extends LispFunction {
|
|||
return newList;
|
||||
}
|
||||
|
||||
private static Cons getLastItem(Cons list) {
|
||||
private Cons getLastItem(Cons list) {
|
||||
Cons tail = list;
|
||||
|
||||
while (tail.getRest().isCons())
|
||||
|
@ -44,33 +72,4 @@ public class APPEND extends LispFunction {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package function.builtin.cons;
|
|||
import static function.builtin.cons.LIST.makeList;
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
import static table.FunctionTable.lookupFunction;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
|
@ -17,25 +18,13 @@ import sexpression.LispNumber;
|
|||
public class LENGTH extends LispFunction {
|
||||
|
||||
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) {
|
||||
return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
|
||||
}
|
||||
|
||||
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 static LENGTH lookupLength() {
|
||||
return (LENGTH) lookupFunction("LENGTH");
|
||||
}
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
@ -53,4 +42,22 @@ public class LENGTH extends LispFunction {
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package table;
|
||||
|
||||
import static function.builtin.cons.APPEND.append;
|
||||
import static function.builtin.cons.LIST.makeList;
|
||||
import static sexpression.Nil.NIL;
|
||||
|
||||
|
@ -8,7 +9,6 @@ import java.util.Map.Entry;
|
|||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import function.builtin.cons.APPEND;
|
||||
import sexpression.Cons;
|
||||
import sexpression.SExpression;
|
||||
import sexpression.Symbol;
|
||||
|
@ -51,7 +51,7 @@ public class SymbolTable {
|
|||
Cons context = NIL;
|
||||
|
||||
for (Entry<String, SExpression> binding : getSortedBindings())
|
||||
context = APPEND.append(context, makeList(makeSymbolValuePair(binding)));
|
||||
context = append(context, makeList(makeSymbolValuePair(binding)));
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
|
@ -43,22 +43,22 @@ public class APPENDTest extends SymbolAndFunctionCleaner {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void appendMakesCopies() {
|
||||
public void appendMakesCopyOfFirstList() {
|
||||
evaluateString("(setq x '(1 2 3))");
|
||||
evaluateString("(setq y '(4 5 6))");
|
||||
evaluateString("(append x y)");
|
||||
|
||||
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)
|
||||
public void appendWithDottedFirstList() {
|
||||
evaluateString("(append '(1 2) (cons 3 4))");
|
||||
}
|
||||
|
||||
@Test(expected = DottedArgumentListException.class)
|
||||
public void appendWithDottedSecondList() {
|
||||
evaluateString("(append (cons 1 2) '(3 4))");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue