Implement append built in function

This commit is contained in:
Mike Cifelli 2018-03-14 20:32:01 -04:00
parent cd7a8361fc
commit ac365918b2
10 changed files with 166 additions and 61 deletions

View File

@ -12,7 +12,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>

View File

@ -37,12 +37,12 @@
accumulator))
accumulator))
(defun append (list-one list-two)
(append-tail (reverse list-one) list-two))
; (defun append (list-one list-two)
; (append-tail (reverse list-one) list-two))
(defun append-tail (reversed-list list-two)
(if (null reversed-list) list-two
(recur (rest reversed-list) (cons (car reversed-list) list-two))))
; (defun append-tail (reversed-list list-two)
; (if (null reversed-list) list-two
; (recur (rest reversed-list) (cons (car reversed-list) list-two))))
(defun reverse (the-list)
(reverse-tail () the-list))

View File

@ -13,12 +13,12 @@ import sexpression.SExpression;
@FunctionNames({ "APPLY" })
public class APPLY extends LispFunction {
private ArgumentValidator argumentValidator;
public static SExpression apply(Cons argumentList) {
return lookupFunction("APPLY").call(argumentList);
}
private ArgumentValidator argumentValidator;
public APPLY(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2);

View File

@ -0,0 +1,67 @@
package function.builtin.cons;
import static sexpression.Nil.NIL;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
@FunctionNames({ "APPEND" })
public class APPEND extends LispFunction {
public static Cons append(Cons firstList, Cons secondList) {
Cons appendedLists = copy(firstList);
if (firstList.isNull())
return copy(secondList);
getLastItem(appendedLists).setRest(copy(secondList));
return appendedLists;
}
private static Cons copy(Cons list) {
if (list.isNull())
return NIL;
Cons newList = new Cons(list.getFirst(), NIL);
Cons builder = newList;
for (Cons iterator = (Cons) list.getRest(); iterator.isCons(); iterator = (Cons) iterator.getRest()) {
builder.setRest(new Cons(iterator.getFirst(), NIL));
builder = (Cons) builder.getRest();
}
return newList;
}
private static Cons getLastItem(Cons list) {
Cons tail = list;
while (tail.getRest().isCons())
tail = (Cons) tail.getRest();
return tail;
}
private ArgumentValidator argumentValidator;
public APPEND(String 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();
return append(firstList, secondList);
}
}

View File

@ -3,7 +3,6 @@ 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;
@ -18,13 +17,25 @@ import sexpression.LispNumber;
public class LENGTH extends LispFunction {
public static BigInteger getLength(Cons list) {
LispNumber length = lookupLength().callWithoutArgumentValidation(makeList(list));
return length.getValue();
return callWithoutArgumentValidation(makeList(list)).getValue();
}
private static LENGTH lookupLength() {
return (LENGTH) lookupFunction("LENGTH");
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 ArgumentValidator argumentValidator;
@ -42,22 +53,4 @@ 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);
}
}

View File

@ -21,6 +21,7 @@ import function.builtin.PRINT;
import function.builtin.SET;
import function.builtin.SYMBOLS;
import function.builtin.SYMBOL_FUNCTION;
import function.builtin.cons.APPEND;
import function.builtin.cons.CONS;
import function.builtin.cons.FIRST;
import function.builtin.cons.LENGTH;
@ -63,6 +64,7 @@ public class FunctionTable {
static {
allBuiltIns.add(AND.class);
allBuiltIns.add(APPEND.class);
allBuiltIns.add(APPLY.class);
allBuiltIns.add(ATOM.class);
allBuiltIns.add(CASE.class);

View File

@ -8,6 +8,7 @@ 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;
@ -50,7 +51,7 @@ public class SymbolTable {
Cons context = NIL;
for (Entry<String, SExpression> binding : getSortedBindings())
context = append(context, makeList(makeSymbolValuePair(binding)));
context = APPEND.append(context, makeList(makeSymbolValuePair(binding)));
return context;
}
@ -63,19 +64,4 @@ public class SymbolTable {
return new Cons(new Symbol(binding.getKey()), makeList(binding.getValue()));
}
// TODO - extract into built in append function
private Cons append(Cons firstList, Cons secondList) {
if (firstList.isNull())
return secondList; // FIXME - copy
Cons appendedList = firstList; // FIXME - copy
Cons appender = appendedList;
for (; !((Cons) appender.getRest()).isNull(); appender = (Cons) appender.getRest()) {}
appender.setRest(secondList); // FIXME - copy
return appendedList;
}
}

View File

@ -0,0 +1,68 @@
package function.builtin.cons;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString;
import org.junit.Test;
import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.TooFewArgumentsException;
import function.ArgumentValidator.TooManyArgumentsException;
import testutil.SymbolAndFunctionCleaner;
public class APPENDTest extends SymbolAndFunctionCleaner {
@Test
public void appendNil() {
String input = "(append () ())";
assertSExpressionsMatch(parseString("nil"), evaluateString(input));
}
@Test
public void appendNilToList() {
String input = "(append () '(1 2 3))";
assertSExpressionsMatch(parseString("(1 2 3)"), evaluateString(input));
}
@Test
public void appendListToNil() {
String input = "(append '(1 2 3) ())";
assertSExpressionsMatch(parseString("(1 2 3)"), evaluateString(input));
}
@Test
public void appendTwoLists() {
String input = "(append '(1 2 3) '(4 5 6))";
assertSExpressionsMatch(parseString("(1 2 3 4 5 6)"), evaluateString(input));
}
@Test
public void appendMakesCopies() {
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(expected = TooManyArgumentsException.class)
public void appendWithTooManyArguments() {
evaluateString("(append () () ())");
}
@Test(expected = TooFewArgumentsException.class)
public void appendWithTooFewArguments() {
evaluateString("(append ())");
}
@Test(expected = BadArgumentTypeException.class)
public void appendWithBadArgumentType() {
evaluateString("(append 1 '(2))");
}
}

View File

@ -51,16 +51,12 @@ public class CONSTest extends SymbolAndFunctionCleaner {
@Test(expected = TooManyArgumentsException.class)
public void consWithTooManyArguments() {
String input = "(cons 1 2 3)";
evaluateString(input);
evaluateString("(cons 1 2 3)");
}
@Test(expected = TooFewArgumentsException.class)
public void consWithTooFewArguments() {
String input = "(cons 1)";
evaluateString(input);
evaluateString("(cons 1)");
}
}

View File

@ -24,13 +24,6 @@
(defun extend-apply (function-name param-list)
(eval (cons function-name param-list)))
(defun append (listA listB)
(cond
((null listA) listB)
(t (cons (first listA) (append (rest listA) listB)))
)
)
(defun second (listA) (first (rest listA)))
(defun third (listA) (first (rest (rest listA))))
(defun fourth (listA) (first (rest (rest (rest listA)))))