2

I'm trying to implement L-system as functions. For example, dragon curve looks like this:

static String F(int n) {
    return n == 0 ? "F" : F(n - 1) + "+" + G(n -1);
}

static String G(int n) {
    return n == 0 ? "G" : F(n - 1) + "-" + G(n -1);
}

@Test
void testDragonCurveAsStaticFunction() {
    assertEquals("F", F(0));
    assertEquals("F+G", F(1));
    assertEquals("F+G+F-G", F(2));
    assertEquals("F+G+F-G+F+G-F-G", F(3));
}

I want to implement this with lambda functions. I got the following implementation by referring to recursion - Implement recursive lambda function using Java 8 - Stack Overflow.

@Test
void testDragonCurveAsLambdaFunction() {
    interface IntStr { String apply(int i); }
    IntStr[] f = new IntStr[2];
    f[0] = n -> n == 0 ? "F" : f[0].apply(n - 1) + "+" + f[1].apply(n - 1);
    f[1] = n -> n == 0 ? "G" : f[0].apply(n - 1) + "-" + f[1].apply(n - 1);
    assertEquals("F", f[0].apply(0));
    assertEquals("F+G", f[0].apply(1));
    assertEquals("F+G+F-G", f[0].apply(2));
    assertEquals("F+G+F-G+F+G-F-G", f[0].apply(3));
}

Is there a way to implement this without using an array?

But I want to create a generic L-System, so I don't want to define a new class, interface or method for the dragon curve.

2 Answers 2

1

I found a solution that uses Map.

@FunctionalInterface
interface IntStr {

    String apply(int n);

    static IntStr cond(String then, IntStr... otherwise) {
        return n -> n == 0 ? then
            : Stream.of(otherwise)
                .map(f -> f.apply(n - 1))
                .collect(Collectors.joining());
    }

    static IntStr constant(String string) {
        return n -> string;
    }

    static IntStr call(Map<String, IntStr> map, String functionName) {
        return n -> map.get(functionName).apply(n);
    }
}

and

@Test
void testDragonCurveAsLambda() {
    Map<String, IntStr> map = new HashMap<>();
    map.put("F", IntStr.cond("F",
        IntStr.call(map, "F"),
        IntStr.constant("+"),
        IntStr.call(map, "G")));
    map.put("G", IntStr.cond("G",
        IntStr.call(map, "F"),
        IntStr.constant("-"),
        IntStr.call(map, "G")));
    IntStr f = map.get("F");
    assertEquals("F", f.apply(0));
    assertEquals("F+G", f.apply(1));
    assertEquals("F+G+F-G", f.apply(2));
    assertEquals("F+G+F-G+F+G-F-G", f.apply(3));
}
Sign up to request clarification or add additional context in comments.

1 Comment

It's a nice example of using static methods in the interface but generally you've replaced array with a map :)
0

I tried implementing it using a class combining two IntStr fields set via constructor or setters:

interface IntStr { 
    String apply(Integer i); 
}
    
class Recursive {
    IntStr other;
    IntStr curr;

    public Recursive() {}
    
    public Recursive(String p1, String s1, String p2, String s2) {
        this.curr  = n -> n == 0 ? p1 : curr.apply(n - 1) + s1 + other.apply(n - 1);
        this.other = n -> n == 0 ? p2 : curr.apply(n - 1) + s2 + other.apply(n - 1);
    }
    
    public void setCurr(String p, String s) {
        this.curr  = n -> n == 0 ? p : curr.apply(n - 1) + s + other.apply(n - 1);
    }
   
    public void setOther(String p, String s) {
        this.other = n -> n == 0 ? p : curr.apply(n - 1) + s + other.apply(n - 1);
    }
}

Then the following code succeeded:

void testDragonCurveAsLambdaFunction() {
    Recursive f1 = new Recursive("F", "+", "G", "-");
// or using setters
//  f1.setCurr("F", "+"); 
//  f1.setOther("G", "-");
    assertEquals("F", f1.curr.apply(0));
    assertEquals("F+G", f1.curr.apply(1));
    assertEquals("F+G+F-G", f1.curr.apply(2));
    assertEquals("F+G+F-G+F+G-F-G", f1.curr.apply(3));
}

An example using AtomicReference as a container for IntStr reference without creating Recursive class:

void testDragonCurveAsLambdaFunction() {
    AtomicReference<IntStr> 
        curr  = new AtomicReference<>(), 
        other = new AtomicReference<>(); 
    
    curr.set(n -> n == 0 ? "F" : curr.get().apply(n - 1) + "+" + other.get().apply(n - 1));
    other.set(n -> n == 0 ? "G" : curr.get().apply(n - 1) + "-" + other.get().apply(n - 1));

    assertEquals("F", curr.get().apply(0));
    assertEquals("F+G", curr.get().apply(1));
    assertEquals("F+G+F-G", curr.get().apply(2));
    assertEquals("F+G+F-G+F+G-F-G", curr.get().apply(3));
}

2 Comments

I want to create a generic L-System, so I don't want to define a new class for the dragon curve.
Well, the local variables must be effectively final in order to be used in lambdas , so anyway you'll have to use a container for IntStr reference such as array, another class, or AtomicReference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.