Implement TCO for the length function

This commit is contained in:
Mike Cifelli 2017-11-12 13:27:04 -05:00
parent 7de348d759
commit 6cd5fb66a4
4 changed files with 69 additions and 10 deletions

View File

@ -1,6 +1,11 @@
(defun problem (n) (load "../lang/functions.lisp")
(if (< n 1) nil
(cons n (problem (- n 1)))))
(setq y (problem 20)) (defun build (n lst)
(setq x (problem 20000)) (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 '(1 1 1 1 0 0 0 0))))

View File

@ -1,12 +1,15 @@
package function.builtin.cons; package function.builtin.cons;
import static function.builtin.cons.LIST.makeList; import static function.builtin.cons.LIST.makeList;
import static recursion.tail.TailCalls.done;
import static recursion.tail.TailCalls.tailCall;
import java.math.BigInteger; import java.math.BigInteger;
import function.ArgumentValidator; import function.ArgumentValidator;
import function.FunctionNames; import function.FunctionNames;
import function.LispFunction; import function.LispFunction;
import recursion.tail.TailCall;
import sexpression.Cons; import sexpression.Cons;
import sexpression.LispNumber; import sexpression.LispNumber;
@ -32,21 +35,21 @@ public class LENGTH extends LispFunction {
public LispNumber call(Cons argumentList) { public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList); argumentValidator.validate(argumentList);
return callTailRecursive(BigInteger.ZERO, argumentList); return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
} }
private LispNumber callWithoutArgumentValidation(Cons argumentList) { private LispNumber callWithoutArgumentValidation(Cons argumentList) {
return callTailRecursive(BigInteger.ZERO, argumentList); return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
} }
private LispNumber callTailRecursive(BigInteger accumulatedLength, Cons argumentList) { private TailCall<LispNumber> callTailRecursive(BigInteger accumulatedLength, Cons argumentList) {
Cons list = (Cons) argumentList.getFirst(); Cons list = (Cons) argumentList.getFirst();
Cons restOfList = makeList(list.getRest()); Cons restOfList = makeList(list.getRest());
if (list.isNull()) if (list.isNull())
return new LispNumber(accumulatedLength); return done(new LispNumber(accumulatedLength));
return callTailRecursive(increment(accumulatedLength), restOfList); return tailCall(() -> callTailRecursive(increment(accumulatedLength), restOfList));
} }
private BigInteger increment(BigInteger number) { private BigInteger increment(BigInteger number) {

View File

@ -0,0 +1,22 @@
package recursion.tail;
import java.util.stream.Stream;
@FunctionalInterface
public interface TailCall<T> {
TailCall<T> apply();
default boolean isComplete() {
return false;
}
default T result() {
throw new UnsupportedOperationException();
}
default T invoke() {
return Stream.iterate(this, TailCall::apply).filter(TailCall::isComplete).findFirst().get().result();
}
}

View File

@ -0,0 +1,29 @@
package recursion.tail;
public class TailCalls {
public static <T> TailCall<T> tailCall(TailCall<T> nextCall) {
return nextCall;
}
public static <T> TailCall<T> done(T value) {
return new TailCall<T>() {
@Override
public boolean isComplete() {
return true;
}
@Override
public T result() {
return value;
}
@Override
public TailCall<T> apply() {
throw new UnsupportedOperationException();
}
};
}
}