Java 中的生成器函数等效

问题描述 投票:0回答:10

我想在 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可以是多维数组或某种形式的嵌套集合。我不确定这将如何运作。有想法吗?

java python iterator generator
10个回答
57
投票

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

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 的机器上,可以在 < 0.03s.

生产 1000 件商品

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


27
投票

Java确实没有yield,但你现在可以使用Java 8流。在我看来,它确实是一个复杂的迭代器,因为它由数组而不是函数支持。鉴于它是一个循环中的循环,可以使用过滤器(跳过空值)和 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());
        }
    }
}

我正在撰写关于生成器实现的文章,作为我关于 Java 8 函数式编程和 Lambda 表达式的博客的一部分,网址为 http://thecannycoder.wordpress.com/,这可能会给您更多关于将 Python 生成器函数转换为 Java 等价物的想法.


7
投票

我希望 Java 有生成器/yield,但因为它不使用迭代器可能是你最好的选择。

在这个例子中我坚持使用数组,但一般来说我建议使用 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
投票

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


3
投票

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

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


2
投票

假设您在问题中描述的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
});

1
投票

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


1
投票

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


1
投票

使用新的

中的Seq,它已经在Java中实现了生成器,您可以像在Python中一样编写自己的生成器函数

public Seq<Integer> generate(List<List<List<Integer>>> array) {
    return c -> {
        for (List<List<Integer>> x : array) {
            if (x != null) {
                for (List<Integer> y : x) {
                    if (y != null) {
                        for (Integer z : y) {
                            if (z != null) {
                                c.accept(z);
                            }
                        }
                    }
                }
            }
        }
    };
}

然后你可以像普通的Java

seq
一样操作/收集返回的
stream


0
投票

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

// 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
© www.soinside.com 2019 - 2024. All rights reserved.