Perform TCO on numerous functions

This commit is contained in:
Mike Cifelli 2017-11-18 18:01:00 -05:00
parent 62b2653b2c
commit a9c13610a2
15 changed files with 96 additions and 56 deletions

View File

@ -1,3 +1,4 @@
|TranscendentalLisp.Recursion||17:56:26 Sat, Nov 18, 2017|
|TranscendentalLisp||16:15:14 Fri, Mar 17, 2017| |TranscendentalLisp||16:15:14 Fri, Mar 17, 2017|
|TranscendentalLisp.Macros||10:10:15 Mon, Mar 13, 2017| |TranscendentalLisp.Macros||10:10:15 Mon, Mar 13, 2017|
|TranscendentalLisp.MacroTests||10:07:00 Mon, Mar 13, 2017| |TranscendentalLisp.MacroTests||10:07:00 Mon, Mar 13, 2017|

View File

@ -0,0 +1,17 @@
---
Test
---
Test recursion capabilities of various functions.
| script | lisp interpreter fixture |
| show | evaluate text | (load "lisp/random/list-builder.lisp") |
| check | evaluate text | (setq big-list (list-doubler '(1 1 1 1 1 1 1 1) 11)) | =~/1\)$/ |
| check | evaluate text | (length big-list) | 16384 |
| check | evaluate text | (length (apply 'list big-list)) | 16384 |
| check | evaluate text | (apply '/ big-list) | 1 |
| check | evaluate text | (apply '* big-list) | 1 |
| check | evaluate text | (apply '+ big-list) | 16384 |
| check | evaluate text | (apply '- big-list) | -16382 |
| check | evaluate text | (apply '= big-list) | T |
| check | evaluate text | (apply '> (decreasing-list 0 10000)) | T |
| check | evaluate text | (apply '< (increasing-list 10000 10000)) | T |

View File

@ -3,3 +3,17 @@
(defun list-doubler (seed times-to-double) (defun list-doubler (seed times-to-double)
(if (< times-to-double 1) seed (if (< times-to-double 1) seed
(recur (append seed seed) (- times-to-double 1)))) (recur (append seed seed) (- times-to-double 1))))
(defun decreasing-list (end size)
(decreasing-list-tail (list end) (+ end 1) size))
(defun decreasing-list-tail (seed end size)
(if (< size 1) seed
(recur (cons (+ (car seed) 1) seed) (+ end 1) (- size 1))))
(defun increasing-list (end size)
(increasing-list-tail (list end) (+ end 1) size))
(defun increasing-list-tail (seed end size)
(if (< size 1) seed
(recur (cons (- (car seed) 1) seed) (- end 1) (- size 1))))

View File

@ -1,11 +0,0 @@
(load "../lang/functions.lisp")
(defun build (n lst)
(if (= n 0) lst
(build (- n 1) (cons (car lst) lst))))
(defun build2 (n lst)
(if (= n 0) lst
(build2 (- n 1) (build 200 lst))))
(length (build2 200 (build2 200 (build2 200 (build2 200 '(1 1 1 1 0 0 0 0))))))

View File

@ -1,11 +0,0 @@
(load "../lang/functions.lisp")
(defun build (n lst)
(if (= n 0) lst
(build (- n 1) (cons (car lst) lst))))
(defun build2 (n lst)
(if (= n 0) lst
(build2 (- n 1) (build 200 lst))))
(build2 200 (build2 200 (build2 200 (build2 200 '(1 1 1 1 0 0 0 0)))))

View File

@ -25,17 +25,7 @@ public class LIST extends LispFunction {
public Cons call(Cons argumentList) { public Cons call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return callRecursive(argumentList); return argumentList;
}
private Cons callRecursive(Cons argumentList) {
if (argumentList.isNull())
return NIL;
SExpression firstArgument = argumentList.getFirst();
Cons remainingArguments = (Cons) argumentList.getRest();
return new Cons(firstArgument, callRecursive(remainingArguments));
} }
} }

View File

@ -2,6 +2,7 @@ package function.builtin.math;
import java.math.BigInteger; import java.math.BigInteger;
import error.LispException;
import function.ArgumentValidator; import function.ArgumentValidator;
import function.FunctionNames; import function.FunctionNames;
import function.LispFunction; import function.LispFunction;
@ -25,7 +26,11 @@ public class DIVIDE extends LispFunction {
public LispNumber call(Cons argumentList) { public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(argumentList); try {
return mathFunction.callTailRecursive(argumentList).invoke();
} catch (ArithmeticException e) {
throw new DivideByZeroException();
}
} }
private LispNumber getReciprocal(LispNumber number) { private LispNumber getReciprocal(LispNumber number) {
@ -36,4 +41,14 @@ public class DIVIDE extends LispFunction {
return new LispNumber(number1.getValue().divide(number2.getValue())); return new LispNumber(number1.getValue().divide(number2.getValue()));
} }
public static class DivideByZeroException extends LispException {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return "divide by zero";
}
}
} }

View File

@ -25,7 +25,7 @@ public class MINUS extends LispFunction {
public LispNumber call(Cons argumentList) { public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(argumentList); return mathFunction.callTailRecursive(argumentList).invoke();
} }
private LispNumber additiveInverse(LispNumber number) { private LispNumber additiveInverse(LispNumber number) {

View File

@ -24,7 +24,7 @@ public class MULTIPLY extends LispFunction {
public LispNumber call(Cons argumentList) { public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(new Cons(ONE, argumentList)); return mathFunction.callTailRecursive(new Cons(ONE, argumentList)).invoke();
} }
private LispNumber multiply(LispNumber number1, LispNumber number2) { private LispNumber multiply(LispNumber number1, LispNumber number2) {

View File

@ -1,8 +1,12 @@
package function.builtin.math; package function.builtin.math;
import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import recursion.TailCall;
import sexpression.Cons; import sexpression.Cons;
import sexpression.LispNumber; import sexpression.LispNumber;
import sexpression.SExpression; import sexpression.SExpression;
@ -18,13 +22,13 @@ class MathFunction {
this.multipleValueOperation = multipleValueOperation; this.multipleValueOperation = multipleValueOperation;
} }
public LispNumber callTailRecursive(Cons argumentList) { public TailCall<LispNumber> callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest(); Cons remainingArguments = (Cons) argumentList.getRest();
SExpression firstArgument = argumentList.getFirst(); SExpression firstArgument = argumentList.getFirst();
LispNumber number1 = (LispNumber) firstArgument; LispNumber number1 = (LispNumber) firstArgument;
if (remainingArguments.isNull()) if (remainingArguments.isNull())
return singleValueOperation.apply(number1); return done(singleValueOperation.apply(number1));
SExpression secondArgument = remainingArguments.getFirst(); SExpression secondArgument = remainingArguments.getFirst();
LispNumber number2 = (LispNumber) secondArgument; LispNumber number2 = (LispNumber) secondArgument;
@ -32,9 +36,9 @@ class MathFunction {
SExpression remainingNumbers = remainingArguments.getRest(); SExpression remainingNumbers = remainingArguments.getRest();
if (remainingNumbers.isNull()) if (remainingNumbers.isNull())
return operationResult; return done(operationResult);
return callTailRecursive(new Cons(operationResult, remainingNumbers)); return tailCall(() -> callTailRecursive(new Cons(operationResult, remainingNumbers)));
} }
} }

View File

@ -24,7 +24,7 @@ public class PLUS extends LispFunction {
public LispNumber call(Cons argumentList) { public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(new Cons(ZERO, argumentList)); return mathFunction.callTailRecursive(new Cons(ZERO, argumentList)).invoke();
} }
private LispNumber add(LispNumber number1, LispNumber number2) { private LispNumber add(LispNumber number1, LispNumber number2) {

View File

@ -1,11 +1,14 @@
package function.builtin.predicate; package function.builtin.predicate;
import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import static sexpression.Symbol.T; import static sexpression.Symbol.T;
import function.ArgumentValidator; import function.ArgumentValidator;
import function.FunctionNames; import function.FunctionNames;
import function.LispFunction; import function.LispFunction;
import recursion.TailCall;
import sexpression.Cons; import sexpression.Cons;
import sexpression.LispNumber; import sexpression.LispNumber;
import sexpression.SExpression; import sexpression.SExpression;
@ -25,14 +28,14 @@ public class NUMERIC_EQUAL extends LispFunction {
public SExpression call(Cons argumentList) { public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return callTailRecursive(argumentList); return callTailRecursive(argumentList).invoke();
} }
private SExpression callTailRecursive(Cons argumentList) { private TailCall<SExpression> callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest(); Cons remainingArguments = (Cons) argumentList.getRest();
if (remainingArguments.isNull()) if (remainingArguments.isNull())
return T; return done(T);
SExpression firstArgument = argumentList.getFirst(); SExpression firstArgument = argumentList.getFirst();
LispNumber number1 = (LispNumber) firstArgument; LispNumber number1 = (LispNumber) firstArgument;
@ -40,9 +43,9 @@ public class NUMERIC_EQUAL extends LispFunction {
LispNumber number2 = (LispNumber) secondArgument; LispNumber number2 = (LispNumber) secondArgument;
if (!isEqual(number1, number2)) if (!isEqual(number1, number2))
return NIL; return done(NIL);
return callTailRecursive(remainingArguments); return tailCall(() -> callTailRecursive(remainingArguments));
} }
private boolean isEqual(LispNumber number1, LispNumber number2) { private boolean isEqual(LispNumber number1, LispNumber number2) {

View File

@ -1,11 +1,14 @@
package function.builtin.predicate; package function.builtin.predicate;
import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import static sexpression.Symbol.T; import static sexpression.Symbol.T;
import function.ArgumentValidator; import function.ArgumentValidator;
import function.FunctionNames; import function.FunctionNames;
import function.LispFunction; import function.LispFunction;
import recursion.TailCall;
import sexpression.Cons; import sexpression.Cons;
import sexpression.LispNumber; import sexpression.LispNumber;
import sexpression.SExpression; import sexpression.SExpression;
@ -25,14 +28,14 @@ public class NUMERIC_GREATER extends LispFunction {
public SExpression call(Cons argumentList) { public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return callTailRecursive(argumentList); return callTailRecursive(argumentList).invoke();
} }
private SExpression callTailRecursive(Cons argumentList) { private TailCall<SExpression> callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest(); Cons remainingArguments = (Cons) argumentList.getRest();
if (remainingArguments.isNull()) if (remainingArguments.isNull())
return T; return done(T);
SExpression firstArgument = argumentList.getFirst(); SExpression firstArgument = argumentList.getFirst();
SExpression secondArgument = remainingArguments.getFirst(); SExpression secondArgument = remainingArguments.getFirst();
@ -40,9 +43,9 @@ public class NUMERIC_GREATER extends LispFunction {
LispNumber number2 = (LispNumber) secondArgument; LispNumber number2 = (LispNumber) secondArgument;
if (!isFirstGreater(number1, number2)) if (!isFirstGreater(number1, number2))
return NIL; return done(NIL);
return callTailRecursive(remainingArguments); return tailCall(() -> callTailRecursive(remainingArguments));
} }
private boolean isFirstGreater(LispNumber number1, LispNumber number2) { private boolean isFirstGreater(LispNumber number1, LispNumber number2) {

View File

@ -1,11 +1,14 @@
package function.builtin.predicate; package function.builtin.predicate;
import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import static sexpression.Symbol.T; import static sexpression.Symbol.T;
import function.ArgumentValidator; import function.ArgumentValidator;
import function.FunctionNames; import function.FunctionNames;
import function.LispFunction; import function.LispFunction;
import recursion.TailCall;
import sexpression.Cons; import sexpression.Cons;
import sexpression.LispNumber; import sexpression.LispNumber;
import sexpression.SExpression; import sexpression.SExpression;
@ -25,14 +28,14 @@ public class NUMERIC_LESS extends LispFunction {
public SExpression call(Cons argumentList) { public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return callTailRecursive(argumentList); return callTailRecursive(argumentList).invoke();
} }
private SExpression callTailRecursive(Cons argumentList) { private TailCall<SExpression> callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest(); Cons remainingArguments = (Cons) argumentList.getRest();
if (remainingArguments.isNull()) if (remainingArguments.isNull())
return T; return done(T);
SExpression firstArgument = argumentList.getFirst(); SExpression firstArgument = argumentList.getFirst();
SExpression secondArgument = remainingArguments.getFirst(); SExpression secondArgument = remainingArguments.getFirst();
@ -40,9 +43,9 @@ public class NUMERIC_LESS extends LispFunction {
LispNumber number2 = (LispNumber) secondArgument; LispNumber number2 = (LispNumber) secondArgument;
if (!isFirstLesser(number1, number2)) if (!isFirstLesser(number1, number2))
return NIL; return done(NIL);
return callTailRecursive(remainingArguments); return tailCall(() -> callTailRecursive(remainingArguments));
} }
private boolean isFirstLesser(LispNumber number1, LispNumber number2) { private boolean isFirstLesser(LispNumber number1, LispNumber number2) {

View File

@ -1,5 +1,6 @@
package function.builtin.math; package function.builtin.math;
import static testutil.TestUtilities.assertIsErrorWithMessage;
import static testutil.TestUtilities.assertSExpressionsMatch; import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString; import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString; import static testutil.TestUtilities.parseString;
@ -8,6 +9,7 @@ import org.junit.Test;
import function.ArgumentValidator.BadArgumentTypeException; import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.TooFewArgumentsException; import function.ArgumentValidator.TooFewArgumentsException;
import function.builtin.math.DIVIDE.DivideByZeroException;
import testutil.SymbolAndFunctionCleaner; import testutil.SymbolAndFunctionCleaner;
public class DIVIDETest extends SymbolAndFunctionCleaner { public class DIVIDETest extends SymbolAndFunctionCleaner {
@ -64,4 +66,14 @@ public class DIVIDETest extends SymbolAndFunctionCleaner {
evaluateString("(/)"); evaluateString("(/)");
} }
@Test(expected = DivideByZeroException.class)
public void divideByZero() {
evaluateString("(/ 2 0)");
}
@Test
public void divideByZeroException_HasCorrectAttributes() {
assertIsErrorWithMessage(new DivideByZeroException());
}
} }