Refactor append code
This commit is contained in:
parent
9d13c3d892
commit
d7f908a01d
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue