Подтвердить что ты не робот

Эквивалент функций генератора в Java

Я хотел бы реализовать Iterator в Java, который ведет себя как следующая функция генератора в Python:

def iterator(array):
   for x in array:
      if x!= None:
        for y in x:
          if y!= None:
            for z in y:
              if z!= None:
                yield z

x на стороне java может быть многомерным массивом или некоторой формой вложенной коллекции. Я не уверен, как это сработает. Идеи?

4b9b3361

Ответ 1

Если бы такая же потребность была написана для него небольшим классом. Вот несколько примеров:

Generator<Integer> simpleGenerator = new Generator<Integer>() {
    public void run() throws InterruptedException {
        yield(1);
        // Some logic here...
        yield(2);
    }
};
for (Integer element : simpleGenerator)
    System.out.println(element);
// Prints "1", then "2".

Возможны также бесконечные генераторы:

Generator<Integer> infiniteGenerator = new Generator<Integer>() {
    public void run() throws InterruptedException {
        while (true)
            yield(1);
    }
};

Класс Generator внутренне работает с потоком для создания элементов. Переопределяя finalize(), он гарантирует, что нити не будут оставаться вокруг, если соответствующий генератор больше не используется.

Производительность, очевидно, не велика, но не слишком убогая. На моей машине с двухъядерным процессором i5 с частотой 2,67 ГГц 1000 элементов могут быть созданы в < 0.03s.

Код находится на GitHub. Там вы также найдете инструкции о том, как включить его в зависимость от Maven/Gradle.

Ответ 2

Действительно, Java не имеет выхода, но теперь вы можете использовать потоки Java 8. IMO это действительно сложный итератор, поскольку он поддерживается массивом, а не функцией. Учитывая это, цикл в цикле в цикле может быть выражен как поток с использованием фильтра (для пропуска нулей) и flatMap для потока внутренней коллекции. Это также касается размера кода Python. Я конвертировал его в итератор для использования в свободное время и распечатывал для демонстрации, но если все, что вы делали, это печать, вы можете закончить последовательность потоков с помощью forEach (System.out:: println) вместо iterator().

public class ArrayIterate
{
    public static void main(String args[])
    {
        Integer[][][] a = new Integer[][][] { { { 1, 2, null, 3 },
                                                null,
                                                { 4 }
                                              },
                                              null,
                                              { { 5 } } };

        Iterator<Object> iterator = Arrays.stream(a)
                                          .filter(ax -> ax != null)
                                          .flatMap(ax -> Arrays.stream(ax)
                                               .filter(ay -> ay != null)
                                               .flatMap(ay -> Arrays.stream(ay)
                                               .filter(az -> az != null)))
                                          .iterator();

        while (iterator.hasNext())
        {
            System.out.println(iterator.next());
        }
    }
}

Я пишу о реализации генераторов как часть своего блога на Java 8 Functional Programming и Lambda Expressions на http://thecannycoder.wordpress.com/, который может дать вы еще несколько идей для преобразования функций генератора Python в эквиваленты Java.

Ответ 3

Я хочу, чтобы у Java был генератор/выход, но поскольку он не использует итераторы, вероятно, ваш лучший выбор.

В этом примере я застрял в массивах, но в целом я бы посоветовал вместо этого использовать Iterable Collection, например. Список. В этом примере я покажу, как довольно легко получить итераторы для массивов:

package example.stackoverflow;

import com.sun.xml.internal.xsom.impl.scd.Iterators;

import java.util.Arrays;
import java.util.Iterator;

public class ArrayGenerator<T> implements Iterable<T> {
    private final T[][][] input;

    public ArrayGenerator(T[][][] input) {
        this.input = input;
    }


    @Override
    public Iterator<T> iterator() {
        return new Iter();
    }

    private class Iter implements Iterator<T> {
        private Iterator<T[][]> x;
        private Iterator<T[]> y;
        private Iterator<T> z;

        {
            x = Arrays.asList(input).iterator();
            y = Iterators.empty();
            z = Iterators.empty();
        }

        @Override
        public boolean hasNext() {
            return z.hasNext() || y.hasNext() || x.hasNext();
        }

        @Override
        public T next() {
            while(! z.hasNext()) {
                while(! y.hasNext()) {
                    y = Arrays.asList(x.next()).iterator();
                }
                z = Arrays.asList(y.next()).iterator();
            }
            return z.next();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("remove not supported");
        }
    }

    public static void main(String[] args) {
        for(Integer i :
                new ArrayGenerator<Integer>(
                        new Integer[][][]{
                          {
                            {1, 2, 3},
                            {4, 5}
                          },
                          {
                            {},
                            {6}
                          },
                          {
                          },
                          {
                            {7, 8, 9, 10, 11}
                          }
                        }
                )) {
            System.out.print(i + ", ");
        }
    }
}

Ответ 4

В Java нет урока, поэтому вы должны делать все это для себя, заканчивая нелепым кодом, как этот:

    for(Integer z : new Iterable<Integer>() {

        @Override
        public Iterator<Integer> iterator() {

            return new Iterator<Integer>() {

                final Integer[][][] d3 = 
                        { { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } },
                        { { 10, 11, 12 }, { 13, 14, 15 }, { 16, 17, 18 } },
                        { { 19, 20, 21 }, { 22, 23, 24 }, { 25, 26, 27 } } };

                int x = 0; 
                int y = 0; 
                int z = 0;

                @Override
                public boolean hasNext() {
                    return !(x==3 && y == 3 && z == 3);
                }

                @Override
                public Integer next() {
                    Integer result = d3[z][y][x];
                    if (++x == 3) {
                        x = 0;
                        if (++y == 3) {
                            y = 0;
                            ++z;
                        }
                    }
                    return result;
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
    }) {
        System.out.println(z);
    }

Но если ваш образец будет содержать более одного yield, это будет еще хуже.

Ответ 5

Перевод с генераторов в стиле Python на итераторы в стиле Java может быть автоматизирован. Если вы готовы принять генерацию кода в процессе сборки, вам может быть интересен этот прототип, который выполняет перевод для вас:

https://github.com/Calvin-L/gen2it

Ответ 6

Нет, Java не имеет "генераторов" или "выход" per se, но та же функциональность доступна с помощью Observer Pattern, Это улучшается при использовании современной реализации, например RxJava. Ваш код будет подписываться на Obserable, и всякий раз, когда он пытается прочитать следующее значение из Observable, он заставит его "сгенерировать" его следующее значение. Observable может поддерживать собственное состояние, как генератор для Python или JavaScript. Когда нет новых значений для чтения, метод "next()" блокирует ожидание появления новых данных. Хороший пример этого можно найти ЗДЕСЬ.