javarecursionoption-typepredicatesupplier

Is there a way to call findMatch recursively?


In this question, I am required to write a findMatch method that takes in a "stream" (IFL is meant to mimic a stream) and finds the first element of the stream that passes a Predicate. My attempt is as follows:

import java.util.function.Predicate;
import java.util.Optional;
import java.util.ArrayList;
import java.util.List;

class IFL<T> {
    Supplier<T> head;
    Supplier<IFL<T>> tail;

    IFL(Supplier<T> head, Supplier<IFL<T>> tail) {
        this.head = head;
        this.tail = tail;
    }

    static <T> IFL<T> of(List<? extends T> list) {
        return new IFL<T>(
            () -> list.get(0),
            () -> IFL.of(list.subList(1, list.size()))) {
        };
    }       

    Optional<T> findMatch(Predicate<? super T> predicate) {
        if (predicate.test(head.get())) {
            return Optional.of(this.head.get());
        } else {
            if (this.tail.get().findMatch(predicate).isPresent()) {
                return this.tail.get().findMatch(predicate);
            }
            return Optional.empty();
        }
    }
}

The problem I am facing is that the else block in my findMatch method seems to only run the return statement instead of recursively calling itself like i believe it should. Is there anyway to ensure that it does?

Update: i have gotten the code to work for the second line of input. However, running the third line of input produces an IndexOutOfBoundsException in jshell.

The input is as follows

    IFL<String> list = IFL.of(Arrays.asList("three", "little", "pigs"))
    list.findMatch(str -> str.length() == 6)
    list.findMatch(str -> str.length() < 4)

Solution

  • You can do like this:

    By checking list size in the constructor and also checking nullity for this.head in the findMatch() method;

    You don't need to check if (this.tail.get().findMatch(predicate).isPresent()) and then call again findMatch method.

    class IFL<T> {
       Supplier<T> head;
       Supplier<IFL<T>> tail;
    
       IFL(Supplier<T> head, Supplier<IFL<T>> tail) {
         this.head = head;
         this.tail = tail;
       }
    
       static <T> IFL<T> of(List<? extends T> list) {
    
         if (list.isEmpty())
             return new IFL<>(null, null);
    
         return new IFL<T>(
                () -> list.get(0),
                () -> IFL.of(list.subList(1, list.size()))) {
         };
       }
    
      Optional<T> findMatch(Predicate<? super T> predicate) {
         if (this.head == null)
            return Optional.empty();
         return predicate.test(head.get()) ?
             Optional.of(this.head.get()) :this.tail.get().findMatch(predicate);
      }
    }