Фибоначчи, использующие Stream и BinaryOperator в Java - PullRequest
1 голос
/ 15 апреля 2019

Я студент и изучаю функциональную Java 8. У меня есть проект, и я не понимаю, как работает этот интерфейс функций. Мой учитель сказал мне «ты должен знать это», и я ищу помощь, чтобы понять эту проблему. Стоит посчитать ряд Фибоначчи

Я получил этот код

StreamUtils.generateRest(Stream.of(1, 1), (a, b) -> a + b)
    .limit(7)
    .forEach(System.out::println);

StreamUtils.generateRest(Stream.of("AAA", "BB", "KKKK"), (a, b) -> a + b)
    .limit(7)
    .forEach(System.out::println);

StreamUtils.generateRest(Stream.of(i -> 0), 
    (BinaryOperator<UnaryOperator<Integer>>) (f, g) -> (x -> x == 0 ? 1 : x * g.apply(x - 1)))
    .limit(10)
    .map(f -> f.apply(7))
    .forEach(System.out::println);

Я сделал что-то подобное, но это не работает

public class StreamUtils<T> {

    public static <T> Stream generateRest(Stream<T> stream, BinaryOperator<T> binaryOperator) {
        return Stream.of(stream.reduce((a, b) -> binaryOperator.apply(a, b)));
    }

}

Кто-то может мне помочь с этим и объяснить, как решить эту проблему?

Ответы [ 2 ]

2 голосов
/ 15 апреля 2019

Чтобы первый пример работал, вам нужно реализовать что-то вроде этого:

private static class StreamUtils<T> {
    public static <T> Stream generateRest(Stream<T> stream, BinaryOperator<T> binaryOperator) {
        return Stream.iterate(stream.toArray(), p -> new Object[]{p[1], binaryOperator.apply((T) p[0], (T) p[1])})
            .flatMap(p -> Stream.of(p[0]));
    }
}

Создает массив из вашего входного потока, затем применяет переданную функцию к двум элементам, сдвигает результат предыдущей итерации в положение 0, так как нам нужны два предыдущих значения для вычисления следующего. И тогда он создает неограниченный поток вычисляемых элементов Фибоначчи.

Вывод:

1
1
2
3
5
8
13

И версия с правильным использованием обобщений, потому что ваша исходная структура создает необработанные типы.

private static class StreamUtils {
    public static <T> Stream<T> generateRest(Stream<T> stream, BinaryOperator<T> binaryOperator) {
        return Stream.iterate(stream.toArray(), p -> new Object[]{p[1], binaryOperator.apply((T) p[0], (T) p[1])})
            .flatMap(p -> Stream.of((T) p[0]));
    }
}
1 голос
/ 15 апреля 2019

Я предполагаю, что наличие более 2 предметов означает A, B, C, A + B, B + C, C + (A + B), (A + B) + (B + C) и т. Д.,и наличие 1 элемента означает A, A + A, A + (A + A), (A + A) + (A + (A + A)) и т. д., где + - бинарный оператор.

По сути, вы превращаете поток в массив, затем используете Stream.generate, и на каждом шаге вы генерируете элемент после того, который у вас есть, сдвигаете массив влево, чтобы соответствовать новому элементу, и возвращаете старый первый элемент (который больше не являетсяв массиве).Обратите внимание, что, поскольку это имеет побочные эффекты (изменение внешнего массива), его нельзя использовать с .parallel().

@SuppressWarnings("unchecked")
public static <T> Stream<T> generateRest(Stream<T> stream, BinaryOperator<T> binaryOperator) {
    T[] t = (T[]) stream.toArray();
    if (t.length == 1) {
        t = (T[]) new Object[] { t[0], binaryOperator.apply(t[0], t[0]) };
    }
    final T[] items = t;
    return Stream.generate(() -> {
        T first = items[0];
        T next = binaryOperator.apply(items[0], items[1]);
        System.arraycopy(items, 1, items, 0, items.length - 1);
        items[items.length - 1] = next;
        return first;
    });
}

Вывод:

1
1
2
3
5
8
13
AAA
BB
KKKK
AAABB
BBKKKK
KKKKAAABB
AAABBBBKKKK
0
0
0
0
0
0
0
0
5040
5040
...