Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FizzBuzz using JDK8 Lambda

Just want to see by JDK8 lambda how small the program can be, my approach is using a result builder:

IntStream.rangeClosed(0 , 100).forEach(i ->{
        StringBuffer bfr= new StringBuffer();
        if(i % 3 == 0 )
            bfr.append("Fizz");
        if(i % 5 == 0 )
            bfr.append("Buzz");
        if(i % 3 != 0 && i % 5 != 0 )
            bfr.append(i);

        System.out.println(bfr.toString());
});

Can anyone try to use predicate instead ? I could not think of a way to do this.

like image 857
Seng Zhe Avatar asked Jun 14 '16 14:06

Seng Zhe


4 Answers

Here are three solutions.

Java 8 with Streams:

IntStream.rangeClosed(0, 100).mapToObj(
        i -> i % 3 == 0 ?
                (i % 5 == 0 ? "FizzBuzz" : "Fizz") :
                (i % 5 == 0 ? "Buzz" : i))
        .forEach(System.out::println);

Java 8 with Eclipse Collections:

IntInterval.zeroTo(100).collect(
        i -> i % 3 == 0 ?
                (i % 5 == 0 ? "FizzBuzz" : "Fizz") :
                (i % 5 == 0 ? "Buzz" : i))
        .each(System.out::println);

Java 8 with Eclipse Collections using Predicates:

Interval.zeroTo(100).collect(
        new CaseFunction<Integer, String>(Object::toString)
            .addCase(i -> i % 15 == 0, e -> "FizzBuzz")
            .addCase(i -> i % 3 == 0, e -> "Fizz")
            .addCase(i -> i % 5 == 0, e -> "Buzz"))
        .each(System.out::println);

Update:

As of the Eclipse Collections 8.0 release, the functional interfaces in Eclipse Collections now extend the equivalent functional interfaces in Java 8. This means that CaseFunction can now be used as a java.util.function.Function, which means it will work with Stream.map(Function). The following example uses CaseFunction with a Stream<Integer>:

IntStream.rangeClosed(0, 100).boxed().map(
        new CaseFunction<Integer, String>(Object::toString)
                .addCase(i -> i % 15 == 0, e -> "FizzBuzz")
                .addCase(i -> i % 3 == 0, e -> "Fizz")
                .addCase(i -> i % 5 == 0, e -> "Buzz"))
        .forEach(System.out::println);

Update:

As of the Eclipse Collections 8.1 release, there is now support for primitive case functions. The code above can now be written as follows, removing the call to boxed. IntCaseFunction implements IntToObjectFunction which extends java.util.function.IntFunction.

IntStream.rangeClosed(0, 100).mapToObj(
        new IntCaseFunction<>(Integer::toString)
                .addCase(i -> i % 15 == 0, e -> "FizzBuzz")
                .addCase(i -> i % 3 == 0, e -> "Fizz")
                .addCase(i -> i % 5 == 0, e -> "Buzz"))
        .forEach(System.out::println);

IntCaseFunction will also work with the IntInterval example, passed as the parameter to the collect method.

Note: I am a committer for Eclipse Collections.

like image 199
Donald Raab Avatar answered Oct 13 '22 09:10

Donald Raab


My version using format.

IntStream.rangeClosed(1, 100).forEach(
    i -> System.out.format("%s%s%n",
                           (i % 3 == 0 ? "Fizz": ""),
                           (i % 5 == 0 ? "Buzz": (i % 3 == 0 ? "" : i))
         )
);
like image 41
h.ozawa Avatar answered Oct 13 '22 11:10

h.ozawa


Recently I was watching Kevlin Henney who was fizzbuzzing different languages on one of his talk. It got me thinking about Java 8 and none of the provided solutions were elegant for me. It is very hard to read chained ternary operators. This is what I came up with:

public class MyApproach {
    private java.util.function.IntPredicate divBy(int number) {
        return i -> i % number == 0;
    }

    public String fizzBuzz(int number) {
        String result = "";
        result += divBy(3).test(number) ? "Fizz" : "";
        result += divBy(5).test(number) ? "Buzz" : "";
        return (result).isEmpty() ? String.valueOf(number) : result;
    }

    public static void main(String[] args) {
        MyApproach myApproach = new MyApproach();
        int startInclusive = Integer.parseInt(args[0]);
        int endInclusive = Integer.parseInt(args[1]);

        IntStream.rangeClosed(startInclusive, endInclusive)
            .mapToObj(myApproach::fizzBuzz)
            .forEach(System.out::println);
    }
}

This way we can throw in some test:

class FizzBuzzTest extends Specification {
    def "parametrized test for fizzBuzz method" () {
        given:
        def myApproach = new MyApproach()

        expect:
        result == myApproach.fizzBuzz(num)

        where:
        result     | num
        "Fizz"     | 21
        "Fizz"     | 123
        "Buzz"     | 50
        "Buzz"     | 250
        "FizzBuzz" | 150
        "13"       | 13
    }
}
like image 34
Sebastian Avatar answered Oct 13 '22 11:10

Sebastian


Version with infinite streams. Origin from Haskell. Often shown in a Kevlin Henney Presentation

public static void main(String[] args) {

    // creates an infinite stream with "Fizz" on every 3rd position
    Stream<String> fizzes = Stream.generate(() -> new String[] {"","","Fizz"}).flatMap(arr -> Arrays.stream(arr));
    // creates an infinite stream with "Buzz" on every 5th position 
    Iterator<String> buzzes = Stream.generate(() -> new String[] {"","","","","Buzz"}).flatMap(arr -> Arrays.stream(arr)).iterator();
    // Infinite number stream as String
    Iterator<String> integers = IntStream.iterate(1, x -> x + 1).mapToObj(value -> Integer.valueOf(value).toString()).iterator();

    // concatenates fizzes with buzzes and return the max string from number or the concatenation
    // FizzBuzz > Fizz > Buzz > 'any number as string' > ""
    String[] array = fizzes.limit(100).map(fizz -> max(integers.next(), fizz+buzzes.next())).toArray(value -> new String[value]);

    System.out.println(Arrays.toString(array));
}

public static String max(String val1, String val2) {
    return val1.compareTo(val2) >= 0 ? val1 : val2;
}

Little more accurate:

public class FizzBuzz {

    public static void main(String[] args) {
        // creates an infinite stream with "Fizz" on every 3rd position
        Stream<String> fizzes = Stream.generate(() -> new String[] {"","","Fizz"}).flatMap(arr -> Arrays.stream(arr));
        // creates an infinite stream with "Buzz" on every 5th position
        Iterator<String> buzzes = Stream.generate(() -> new String[] {"","","","","Buzz"}).flatMap(arr -> Arrays.stream(arr)).iterator();
        // Concatenates fizzes with buzzes
        Iterator<String> words = fizzes.map(fizz -> fizz+buzzes.next()).iterator();
        // Infinite number stream as String
        Stream<String> numbers = IntStream.iterate(1, x -> x + 1).mapToObj(Integer::toString);
        // Choice operation word or number
        BinaryOperator<String> choice1 = FizzBuzz::max;
        BinaryOperator<String> choice2 = FizzBuzz::emptyAsNumberOrWord;

        numbers.limit(100).map(number -> choice1.apply(number, words.next())).forEach(System.out::println);
    }

    public static String max(String number, String word) {
        return number.compareTo(word) >= 0 ? number : word;
    }

    public static String emptyAsNumberOrWord(String number, String word) {
        return word.isEmpty() ? number : word;
    }
}
like image 21
E. Stadlbauer Avatar answered Oct 13 '22 11:10

E. Stadlbauer