package function.builtin.cons; import static function.builtin.cons.LIST.makeList; import static recursion.TailCalls.done; import static recursion.TailCalls.tailCall; import java.math.BigInteger; import function.ArgumentValidator; import function.FunctionNames; import function.LispFunction; import recursion.TailCall; import sexpression.Cons; import sexpression.LispNumber; @FunctionNames({ "LENGTH" }) public class LENGTH extends LispFunction { public static BigInteger getLength(Cons list) { return callWithoutArgumentValidation(makeList(list)).getValue(); } private static LispNumber callWithoutArgumentValidation(Cons argumentList) { return callTailRecursive(BigInteger.ZERO, argumentList).invoke(); } private static TailCall 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; public LENGTH(String name) { this.argumentValidator = new ArgumentValidator(name); this.argumentValidator.setExactNumberOfArguments(1); this.argumentValidator.setEveryArgumentExpectedType(Cons.class); } @Override public LispNumber call(Cons argumentList) { argumentValidator.validate(argumentList); return callTailRecursive(BigInteger.ZERO, argumentList).invoke(); } }