web-dev-qa-db-ja.com

Javaで同等のジェネレーター関数

Pythonの次のジェネレーター関数のように動作するJavaでIteratorを実装したいと思います。

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

Java側のxは、多次元配列またはネストされたコレクションの何らかの形式である可能性があります。これがどのように機能するかはわかりません。

37
Eqbal

同じニーズがあったため、そのための小さなクラスを作成しました。ここではいくつかの例を示します。

_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()をオーバーライドすることにより、対応するGeneratorが使用されなくなった場合にスレッドが回避されないようにします。

パフォーマンスは明らかに素晴らしいものではありませんが、みすぼらしいものでもありません。デュアルコアi5 CPU @ 2.67 GHzのマシンでは、0.03秒以内に1000個のアイテムを生産できます。

コードは GitHub にあります。また、Maven/Gradleの依存関係として含める方法についての説明もあります。

42

確かにJavaは収量がありませんが、Java 8ストリームを使用できます。IMOは、関数ではなく配列によって支えられているため、実際には複雑なイテレータです。ループ内のループであるとすると、フィルター(nullをスキップする)とflatMapを使用して内部コレクションをストリームするStreamとして表現できます。また、約Pythonコードのサイズ余暇に使用するイテレータに変換し、印刷してデモンストレーションを行いましたが、印刷するだけであれば、iterator()の代わりにforEach(System.out :: println)でストリームシーケンスを終了できます。

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 and Lambda Expressions at http://thecannycoder.wordpress.com/ のブログの一部としてジェネレーターの実装について書いていますPythonジェネレーター関数をJavaに相当するものに変換するためのアイデアをもう少し教えてください。

17
TheCannyCoder

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 + ", ");
        }
    }
}
6
Spen

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が含まれる場合、さらに悪化します。

4
Arne

PythonスタイルのジェネレーターからJavaスタイルのイテレーターへの変換は自動化できます。ビルドプロセスでコード生成を受け入れる場合は、翻訳を行うこのプロトタイプツールに興味があるかもしれません。

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

1
Calvin