From 69347251795c6971e2a8d0987c4b3511e6ef98d1 Mon Sep 17 00:00:00 2001 From: Fabio Scotto di Santolo Date: Fri, 24 Jan 2020 15:38:05 +0100 Subject: [PATCH] Tail recursion abstraction --- .../src/main/java/org/gym/fp/fpjava/Main.java | 2 + .../org/gym/fp/fpjava/demo/RecursionDemo.java | 23 ++++++ .../java/org/gym/fp/fpjava/type/TailCall.java | 71 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 functional-programming-java/src/main/java/org/gym/fp/fpjava/demo/RecursionDemo.java create mode 100644 functional-programming-java/src/main/java/org/gym/fp/fpjava/type/TailCall.java diff --git a/functional-programming-java/src/main/java/org/gym/fp/fpjava/Main.java b/functional-programming-java/src/main/java/org/gym/fp/fpjava/Main.java index 9ffe3b1..1f1f941 100644 --- a/functional-programming-java/src/main/java/org/gym/fp/fpjava/Main.java +++ b/functional-programming-java/src/main/java/org/gym/fp/fpjava/Main.java @@ -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(); } } diff --git a/functional-programming-java/src/main/java/org/gym/fp/fpjava/demo/RecursionDemo.java b/functional-programming-java/src/main/java/org/gym/fp/fpjava/demo/RecursionDemo.java new file mode 100644 index 0000000..9cf9027 --- /dev/null +++ b/functional-programming-java/src/main/java/org/gym/fp/fpjava/demo/RecursionDemo.java @@ -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 addRec(int x, int y) { + return y == 0 + ? ret(x) + : suspend(() -> addRec(x + 1, y - 1)); + } +} diff --git a/functional-programming-java/src/main/java/org/gym/fp/fpjava/type/TailCall.java b/functional-programming-java/src/main/java/org/gym/fp/fpjava/type/TailCall.java new file mode 100644 index 0000000..a84bb2e --- /dev/null +++ b/functional-programming-java/src/main/java/org/gym/fp/fpjava/type/TailCall.java @@ -0,0 +1,71 @@ +package org.gym.fp.fpjava.type; + +public abstract class TailCall { + + private TailCall() { + } + + public static TailCall ret(T t) { + return new Return<>(t); + } + + public static TailCall suspend(Supplier> s) { + return new Suspend<>(s); + } + + public abstract TailCall resume(); + + public abstract T eval(); + + public abstract boolean isSuspend(); + + private static class Return extends TailCall { + private final T t; + + public Return(T t) { + this.t = t; + } + + @Override + public TailCall resume() { + throw new IllegalStateException("Return has no resume"); + } + + @Override + public T eval() { + return t; + } + + @Override + public boolean isSuspend() { + return false; + } + } + + private static class Suspend extends TailCall { + private final Supplier> resume; + + public Suspend(Supplier> resume) { + this.resume = resume; + } + + @Override + public TailCall resume() { + return resume.get(); + } + + @Override + public T eval() { + TailCall tailRec = this; + while (tailRec.isSuspend()) { + tailRec = tailRec.resume(); + } + return tailRec.eval(); + } + + @Override + public boolean isSuspend() { + return true; + } + } +}