Tail recursion abstraction

This commit is contained in:
Fabio Scotto di Santolo
2020-01-24 15:38:05 +01:00
parent 8badda88cf
commit 6934725179
3 changed files with 96 additions and 0 deletions

View File

@@ -2,12 +2,14 @@ package org.gym.fp.fpjava;
import org.gym.fp.fpjava.demo.AbstractControlStructureDemo;
import org.gym.fp.fpjava.demo.FunctionDemo;
import org.gym.fp.fpjava.demo.RecursionDemo;
public class Main {
public static void main(String[] args) {
FunctionDemo.run();
AbstractControlStructureDemo.run();
RecursionDemo.run();
}
}

View File

@@ -0,0 +1,23 @@
package org.gym.fp.fpjava.demo;
import org.gym.fp.fpjava.type.TailCall;
import static org.gym.fp.fpjava.type.TailCall.ret;
import static org.gym.fp.fpjava.type.TailCall.suspend;
public class RecursionDemo {
public static void run() {
System.out.println(add(10, 1000));
}
public static int add(int x, int y) {
return addRec(x, y).eval();
}
private static TailCall<Integer> addRec(int x, int y) {
return y == 0
? ret(x)
: suspend(() -> addRec(x + 1, y - 1));
}
}

View File

@@ -0,0 +1,71 @@
package org.gym.fp.fpjava.type;
public abstract class TailCall<T> {
private TailCall() {
}
public static <T> TailCall<T> ret(T t) {
return new Return<>(t);
}
public static <T> TailCall<T> suspend(Supplier<TailCall<T>> s) {
return new Suspend<>(s);
}
public abstract TailCall<T> resume();
public abstract T eval();
public abstract boolean isSuspend();
private static class Return<T> extends TailCall<T> {
private final T t;
public Return(T t) {
this.t = t;
}
@Override
public TailCall<T> resume() {
throw new IllegalStateException("Return has no resume");
}
@Override
public T eval() {
return t;
}
@Override
public boolean isSuspend() {
return false;
}
}
private static class Suspend<T> extends TailCall<T> {
private final Supplier<TailCall<T>> resume;
public Suspend(Supplier<TailCall<T>> resume) {
this.resume = resume;
}
@Override
public TailCall<T> resume() {
return resume.get();
}
@Override
public T eval() {
TailCall<T> tailRec = this;
while (tailRec.isSuspend()) {
tailRec = tailRec.resume();
}
return tailRec.eval();
}
@Override
public boolean isSuspend() {
return true;
}
}
}