package function.builtin.cons; import static function.builtin.cons.LIST.makeList; import static recursion.tail.TailCalls.done; import static recursion.tail.TailCalls.tailCall; import java.math.BigInteger; import function.ArgumentValidator; import function.FunctionNames; import function.LispFunction; import recursion.tail.TailCall; import sexpression.Cons; import sexpression.LispNumber; @FunctionNames({ "LENGTH" }) public class LENGTH extends LispFunction { public static BigInteger getLength(Cons list) { LENGTH lengthFunction = new LENGTH("LENGTH"); LispNumber length = lengthFunction.callWithoutArgumentValidation(makeList(list)); return length.getValue(); } 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(); } private LispNumber callWithoutArgumentValidation(Cons argumentList) { return callTailRecursive(BigInteger.ZERO, argumentList).invoke(); } private 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 BigInteger increment(BigInteger number) { return number.add(BigInteger.ONE); } }