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.Macros||10:10:15 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)
|
||||
(if (< times-to-double 1) seed
|
||||
(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) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
return callRecursive(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));
|
||||
return argumentList;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package function.builtin.math;
|
|||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import error.LispException;
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
|
@ -25,7 +26,11 @@ public class DIVIDE extends LispFunction {
|
|||
public LispNumber call(Cons 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) {
|
||||
|
@ -36,4 +41,14 @@ public class DIVIDE extends LispFunction {
|
|||
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) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
return mathFunction.callTailRecursive(argumentList);
|
||||
return mathFunction.callTailRecursive(argumentList).invoke();
|
||||
}
|
||||
|
||||
private LispNumber additiveInverse(LispNumber number) {
|
||||
|
|
|
@ -24,7 +24,7 @@ public class MULTIPLY extends LispFunction {
|
|||
public LispNumber call(Cons 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) {
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import recursion.TailCall;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import sexpression.SExpression;
|
||||
|
@ -18,13 +22,13 @@ class MathFunction {
|
|||
this.multipleValueOperation = multipleValueOperation;
|
||||
}
|
||||
|
||||
public LispNumber callTailRecursive(Cons argumentList) {
|
||||
public TailCall<LispNumber> callTailRecursive(Cons argumentList) {
|
||||
Cons remainingArguments = (Cons) argumentList.getRest();
|
||||
SExpression firstArgument = argumentList.getFirst();
|
||||
LispNumber number1 = (LispNumber) firstArgument;
|
||||
|
||||
if (remainingArguments.isNull())
|
||||
return singleValueOperation.apply(number1);
|
||||
return done(singleValueOperation.apply(number1));
|
||||
|
||||
SExpression secondArgument = remainingArguments.getFirst();
|
||||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
|
@ -32,9 +36,9 @@ class MathFunction {
|
|||
SExpression remainingNumbers = remainingArguments.getRest();
|
||||
|
||||
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) {
|
||||
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) {
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package function.builtin.predicate;
|
||||
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
import static sexpression.Nil.NIL;
|
||||
import static sexpression.Symbol.T;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import recursion.TailCall;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import sexpression.SExpression;
|
||||
|
@ -25,14 +28,14 @@ public class NUMERIC_EQUAL extends LispFunction {
|
|||
public SExpression call(Cons 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();
|
||||
|
||||
if (remainingArguments.isNull())
|
||||
return T;
|
||||
return done(T);
|
||||
|
||||
SExpression firstArgument = argumentList.getFirst();
|
||||
LispNumber number1 = (LispNumber) firstArgument;
|
||||
|
@ -40,9 +43,9 @@ public class NUMERIC_EQUAL extends LispFunction {
|
|||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
|
||||
if (!isEqual(number1, number2))
|
||||
return NIL;
|
||||
return done(NIL);
|
||||
|
||||
return callTailRecursive(remainingArguments);
|
||||
return tailCall(() -> callTailRecursive(remainingArguments));
|
||||
}
|
||||
|
||||
private boolean isEqual(LispNumber number1, LispNumber number2) {
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package function.builtin.predicate;
|
||||
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
import static sexpression.Nil.NIL;
|
||||
import static sexpression.Symbol.T;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import recursion.TailCall;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import sexpression.SExpression;
|
||||
|
@ -25,14 +28,14 @@ public class NUMERIC_GREATER extends LispFunction {
|
|||
public SExpression call(Cons 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();
|
||||
|
||||
if (remainingArguments.isNull())
|
||||
return T;
|
||||
return done(T);
|
||||
|
||||
SExpression firstArgument = argumentList.getFirst();
|
||||
SExpression secondArgument = remainingArguments.getFirst();
|
||||
|
@ -40,9 +43,9 @@ public class NUMERIC_GREATER extends LispFunction {
|
|||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
|
||||
if (!isFirstGreater(number1, number2))
|
||||
return NIL;
|
||||
return done(NIL);
|
||||
|
||||
return callTailRecursive(remainingArguments);
|
||||
return tailCall(() -> callTailRecursive(remainingArguments));
|
||||
}
|
||||
|
||||
private boolean isFirstGreater(LispNumber number1, LispNumber number2) {
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package function.builtin.predicate;
|
||||
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
import static sexpression.Nil.NIL;
|
||||
import static sexpression.Symbol.T;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import recursion.TailCall;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import sexpression.SExpression;
|
||||
|
@ -25,14 +28,14 @@ public class NUMERIC_LESS extends LispFunction {
|
|||
public SExpression call(Cons 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();
|
||||
|
||||
if (remainingArguments.isNull())
|
||||
return T;
|
||||
return done(T);
|
||||
|
||||
SExpression firstArgument = argumentList.getFirst();
|
||||
SExpression secondArgument = remainingArguments.getFirst();
|
||||
|
@ -40,9 +43,9 @@ public class NUMERIC_LESS extends LispFunction {
|
|||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
|
||||
if (!isFirstLesser(number1, number2))
|
||||
return NIL;
|
||||
return done(NIL);
|
||||
|
||||
return callTailRecursive(remainingArguments);
|
||||
return tailCall(() -> callTailRecursive(remainingArguments));
|
||||
}
|
||||
|
||||
private boolean isFirstLesser(LispNumber number1, LispNumber number2) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import static testutil.TestUtilities.assertIsErrorWithMessage;
|
||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
@ -8,6 +9,7 @@ import org.junit.Test;
|
|||
|
||||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.builtin.math.DIVIDE.DivideByZeroException;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
||||
public class DIVIDETest extends SymbolAndFunctionCleaner {
|
||||
|
@ -64,4 +66,14 @@ public class DIVIDETest extends SymbolAndFunctionCleaner {
|
|||
evaluateString("(/)");
|
||||
}
|
||||
|
||||
@Test(expected = DivideByZeroException.class)
|
||||
public void divideByZero() {
|
||||
evaluateString("(/ 2 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void divideByZeroException_HasCorrectAttributes() {
|
||||
assertIsErrorWithMessage(new DivideByZeroException());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue