Perform TCO on numerous functions
This commit is contained in:
parent
62b2653b2c
commit
a9c13610a2
|
@ -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|
|
||||||
|
|
|
@ -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 |
|
|
@ -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))))
|
||||||
|
|
|
@ -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))))))
|
|
|
@ -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)))))
|
|
|
@ -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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue