使用 JDK8 Lambda 的 FizzBu​​zz

FizzBuzz using JDK8 Lambda

只是想通过 JDK8 lambda 看看程序能有多小,我的方法是使用结果生成器:

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());
});

任何人都可以尝试改用谓词吗?我想不出办法来做到这一点。

你的代码可以这样用谓词重写:

        IntPredicate dividesBy3 = i -> i % 3 == 0;
        IntPredicate dividesBy5 = i -> i % 5 == 0;
        IntPredicate doesntDivide = dividesBy3.negate().and(dividesBy5.negate());

        IntStream.rangeClosed(0, 100).forEach(i -> {
            StringBuffer bfr = new StringBuffer();

            if (dividesBy3.test(i)) bfr.append("Fizz");
            if (dividesBy5.test(i)) bfr.append("Buzz");
            if (doesntDivide.test(i)) bfr.append(i);

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

但实际上并没有变小:)

这是三个解决方案。

Java 8 条流:

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 与 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 使用谓词的 Eclipse 集合:

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);

更新:

从 Eclipse Collections 8.0 release 开始,Eclipse Collections 中的功能接口现在扩展了 Java 8 中的等效功能接口。这意味着 CaseFunction 现在可以用作java.util.function.Function,这意味着它将与 Stream.map(Function) 一起使用。以下示例使用 CaseFunctionStream<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);

更新:

自 Eclipse Collections 8.1 release 起,现在支持原始 case 函数。上面的代码现在可以写成如下,删除对 boxed 的调用。 IntCaseFunction 实现 IntToObjectFunction 扩展 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 也将与 IntInterval 示例一起工作,作为参数传递给 collect 方法。

注意:我是 Eclipse Collections 的提交者。

最近我在看 Kevlin Henney,他在一次演讲中用不同的语言发出嘶嘶声。这让我想到 Java 8 和 none 提供的解决方案对我来说很优雅。很难阅读链式三元运算符。这是我想出的:

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);
    }
}

这样我们可以进行一些测试:

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
    }
}

我的版本使用格式。

IntStream.rangeClosed(1, 100).forEach(
    i -> System.out.format("%s%s%n",
                           (i % 3 == 0 ? "Fizz": ""),
                           (i % 5 == 0 ? "Buzz": (i % 3 == 0 ? "" : i))
         )
);

无限流版本。来自 Haskell。经常出现在 Kevlin Henney 的演讲中

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;
}

更准确一点:

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;
    }
}

Stream API implementation:

import java.util.stream.Stream;

public class FizzBuzz {
    public static void main(String[] args) {

        final String FIZZ = "Fizz";
        final String BUZZ = "Buzz";
        Stream.iterate(1, n -> n + 1)
                .limit(100)
                .forEachOrdered(item -> {
                    if (item % 3 == 0 && item % 5 == 0) {
                        System.out.println(FIZZ + " " + BUZZ);
                        return;
                    } else if (item % 3 == 0) {
                        System.out.println(FIZZ);
                        return;
                    } else if (item % 5 == 0) {
                        System.out.println(BUZZ);
                        return;
                    }

                    System.out.println(item);
                });
    }
}
IntStream.rangeClosed(1, 100)
      .mapToObj(x -> x % 15 == 0 ? "FizzBuzz" : x % 5 == 0 ? "Buzz" : x % 3 == 0 ? "Fizz" : x)
      .forEach(System.out::println);
public class FizzBuzz {

    private static final int RANGE_START = 1;
    private static final int RANGE_END = 100;

    public static void main(String[] args) {
        IntStream.rangeClosed(RANGE_START, RANGE_END)
                .mapToObj(FizzBuzz::FizzBuzzMapper)
                .forEach(System.out::println);
    }

    private static String FizzBuzzMapper(final int number) {
        return PAIR_LIST.stream()
                .filter(p -> p.getKey().test(number))
                .map(Pair::getValue)
                .findFirst()
                .orElse(String.valueOf(number));
    }

    private static final List<Pair<Predicate<Integer>, String>> PAIR_LIST = List.of(
            Pair.of(i -> integerPredicate(15).test(i), "FizzBuzz"),
            Pair.of(i -> integerPredicate(5).test(i), "Buzz"),
            Pair.of(i -> integerPredicate(3).test(i), "Fizz")
    );

    private static Predicate<Integer> integerPredicate(final int divisor) {
        return i -> i % divisor == 0;
    }
}
public class FizzBuzz {

    public static void main(String[] args) {
        IntStream.rangeClosed(1, 100).forEach(FizzBuzz::check);
    }

    public static void check(int n) {
        System.out.println((n % 3 == 0 ? "Fizz" : "") + (n % 5 == 0 ? "Buzz" : ""));
    }

}

我的功能方法版本,固然不是很短。

既没有使用else,也没有使用三元运算符。但它可以通过向 fizzBu​​zzers 地图添加新条件来扩展。

public class FizzBuzz {

    public static void main(String[] args) {
        final Map<Integer, String> fizzBuzzers = new HashMap<>();
        fizzBuzzers.put(3,  "Fizz");
        fizzBuzzers.put(5,  "Buzz");
        fizzBuzzers.put(8,  "Bingo");
        fizzBuzzers.put(17, "Dingo");
        
        IntStream.rangeClosed(1, 100)
            .forEach(i -> System.out.println(fizzBuzz(i, fizzBuzzers)));

    }

    private static String fizzBuzz(int i, Map<Integer, String> fizzBuzzers) {
        final List<Map.Entry<Integer, String>> multiBuzz = fizzBuzzers.entrySet().stream()
                .filter(entry -> i % entry.getKey() == 0)
                .collect(Collectors.toList());
        
        if (!multiBuzz.isEmpty()) {
            return multiBuzz.stream()
                    .map(Map.Entry::getValue)
                    .collect(Collectors.joining());
        }
        return String.valueOf(i);
    }