【问题标题】:Generator functions equivalent in JavaJava中等效的生成器函数
【发布时间】:2012-07-19 04:28:01
【问题描述】:

我想在 Java 中实现一个 Iterator,其行为类似于 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
java 端的

x 可以是多维数组或某种形式的嵌套集合。我不确定这将如何工作。想法?

【问题讨论】:

  • 那么,基本上你想遍历 z 维度中的值?
  • 是的,并且可以选择使用一些谓词过滤器,如图所示。
  • 我现在有点懒得写响应,但基本上你需要一个自定义迭代器。
  • 顺便说一句,你也可以写成(z for x in array if x is not None for y in x if y is not None for z in y if z is not None)
  • 另一种方式 - 编写“扁平嵌套”迭代器,如:codereview.stackexchange.com/questions/32827/…

标签: java python iterator generator


【解决方案1】:

您可以使用流的迭代器来完成此操作。

// Save the iterator of a stream that generates fib sequence
Iterator<Integer> myGenerator = Stream
        .iterate(new Integer[]{ 1, 1 }, x -> new Integer[] { x[1], x[0] + x[1] })
        .map(x -> x[0]).iterator();

// Print the first 5 elements
for (int i = 0; i < 5; i++) {
    System.out.println(myGenerator.next());
}

System.out.println("done with first iteration");

// Print the next 5 elements
for (int i = 0; i < 5; i++) {
    System.out.println(myGenerator.next());
}

输出:

1
1
2
3
5
done with first iteration
8
13
21
34
55

【讨论】:

    【解决方案2】:

    游戏很晚,但我想提供我的解决方案作为参考。 https://github.com/tsi-software/JavaGenerator 一个 Java 类,它允许您编写与 Python 和 C# 尽可能相似的“生成器”代码。

    【讨论】:

      【解决方案3】:

      假设您在问题中描述的 Python 数据结构可以使用以下 Java 类型进行描述:

      List<List<List<T>>>;
      

      你想在这样的操作中使用它:

      for (T z : iterator(array)) {
        // do something with z
      }
      

      如果是这样,那么可以使用 Java 8 流非常简单地实现您的 Python iterator()

      public <T> Iterable<T> iterator(List<List<List<T>>> array) {
        return array.stream()
            .filter(Objects::nonNull) // -> emits stream of non-null `x`s
          .flatMap(x -> x.stream()).filter(Objects::nonNull) // -> emits […] `y`s
          .flatMap(y -> y.stream()).filter(Objects::nonNull) // -> emits […] `z`s
          .collect(Collectors.toList()); // get list of non-null `z`s to iterate on
      }
      

      当然,你不能收集结果并输出一个流以进行进一步的流处理(人们告诉我这是个好主意):

      public <T> Stream<T> streamContent(List<List<List<T>>> array) {
        return array.stream()
            .filter(Objects::nonNull) // -> emits stream of non-null `x`s
          .flatMap(x -> x.stream()).filter(Objects::nonNull) // -> emits […] `y`s
          .flatMap(y -> y.stream()).filter(Objects::nonNull); // -> emits […] `z`s
      }
      
      // ...
      
      streamContent(array).forEach(z -> {
        // do something with z
      });
      

      【讨论】:

        【解决方案4】:

        从 Python 风格的生成器到 Java 风格的迭代器的转换可以自动化。如果您愿意在构建过程中接受代码生成,您可能会对这个为您进行翻译的原型工具感兴趣:

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

        【讨论】:

          【解决方案5】:

          不,Java 本身没有“生成器”或“产量”,但使用 Observer Pattern 可以使用相同的功能。当使用像RxJava 这样的现代实现时,这一点得到了增强。您的代码将订阅 Obserable 并且每当它尝试从 Observable 读取下一个值时,它都会导致它“生成”它的下一个值。 Observable 可以像 Python 或 JavaScript 的生成器一样维护自己的状态。当没有要读取的新值时,“next()”方法将阻止等待新数据可用。 HERE 就是一个很好的例子。

          【讨论】:

          • 我不知道为什么这个投票如此艰难。也许添加一个例子?
          【解决方案6】:

          有同样的需求,所以为它写了一个小类。以下是一些示例:

          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 CPU @ 2.67 GHz 的机器上,可以在

          代码位于GitHub。在那里,您还可以找到有关如何将其作为 Maven/Gradle 依赖项包含在内的说明。

          【讨论】:

          • 你在 maven repo 上实现了生成器吗?
          • 还没有。如果我创建一个,我会在这里告诉你。
          • 我已经启动了在 Maven Central 上发布代码的过程,正在等待 Maven Central 操作员的授权。
          • 代码现在在 GitHub 上并发布在公共 Maven 存储库上。我已经更新了上面的答案。
          • 你使用线程组。也许你可以重构考虑有效 Java 的第 73 条:“避免线程组”
          【解决方案7】:

          确实 Java 没有收益,但您现在可以使用 Java 8 流。 IMO 它确实是一个复杂的迭代器,因为它由一个数组支持,而不是一个函数。鉴于它是循环中的循环,循环中的循环可以表示为使用过滤器(跳过空值)和 flatMap 流式处理内部集合的 Stream。它也与 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());
                  }
              }
          }
          

          我在 http://thecannycoder.wordpress.com/ 上关于 Java 8 函数式编程和 Lambda 表达式的博客中写了关于生成器的实现的文章,这可能会给您一些将 Python 生成器函数转换为 Java 等效函数的更多想法。

          【讨论】:

          • 您可以filter(Objects::nonNull) 而不是为此编写新的 lambda。
          • @Jon O,这两个参数都需要 16 个字符,并且具有相同的运行时性能,但是答案中的版本可以说更清晰,不需要额外的导入。
          • 这是一个 stdlib 导入,所以我并没有把它算在内,但答案是为三个不同的命名参数写 3 倍(这可能会创建三个不同的 lambda 类?)。我的方式也不必担心 lambda 参数名称冲突。 =)
          【解决方案8】:

          我希望 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 + ", ");
                  }
              }
          }
          

          【讨论】:

            【解决方案9】:

            Java没有yield,所以你必须自己做所有这些事情,最终得到像这样荒谬的代码:

                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,结果会更糟。

            【讨论】:

              猜你喜欢
              • 2016-05-12
              • 2021-09-15
              • 1970-01-01
              • 2015-02-09
              • 1970-01-01
              • 2021-09-19
              • 2011-08-28
              • 2021-01-09
              • 1970-01-01
              相关资源
              最近更新 更多