Java 是否有相当于 C# 的“yield”?关键词?

发布于 2024-08-16 12:53:37 字数 89 浏览 11 评论 0原文

我知道 Java 本身没有直接的等价物,但也许有第三方?

真的很方便。目前我想实现一个迭代器,它生成树中的所有节点,大约有五行带有yield的代码。

I know there is no direct equivalent in Java itself, but perhaps a third party?

It is really convenient. Currently I'd like to implement an iterator that yields all nodes in a tree, which is about five lines of code with yield.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

十年不长 2024-08-23 12:53:37

我知道的两个选项是 Aviad Ben Dov 2007 年的 infomancers-collections 库 和 < a href="http://svn.jimblackler.net/jimblackler/trunk/IdeaProjects/YieldAdapter/" rel="noreferrer">Jim Blackler 2008 年的 YieldAdapter 库(另一个答案中也提到了)。

两者都允许您在 Java 中使用类似 yield return 的结构编写代码,因此两者都能满足您的要求。两者之间的显着差异是:

Mechanics

Aviad 的库使用字节码操作,而 Jim 的库使用多线程。根据您的需求,每种方法可能都有自己的优点和缺点。 Aviad 的解决方案可能更快,而 Jim 的解决方案更便携(例如,我认为 Aviad 的库不适用于 Android)。

Interface

Aviad 的库有一个更清晰的界面 - 这里有一个例子:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

而 Jim 的则复杂得多,需要您adept一个通用的Collector,它有一个 collect(ResultHandler) 方法...呃。但是,您可以使用类似于 Zoom Information 提供的 Jim 代码包装器,这大大简化了

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

Aviad 的解决方案是 BSD。

Jim 的解决方案是公共领域的,上面提到的它的包装器也是公共领域的。

The two options I know of is Aviad Ben Dov's infomancers-collections library from 2007 and Jim Blackler's YieldAdapter library from 2008 (which is also mentioned in the other answer).

Both will allow you to write code with yield return-like construct in Java, so both will satisfy your request. The notable differences between the two are:

Mechanics

Aviad's library is using bytecode manipulation while Jim's uses multithreading. Depending on your needs, each may have its own advantages and disadvantages. It's likely Aviad's solution is faster, while Jim's is more portable (for example, I don't think Aviad's library will work on Android).

Interface

Aviad's library has a cleaner interface - here's an example:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

While Jim's is way more complicated, requiring you to adept a generic Collector which has a collect(ResultHandler) method... ugh. However, you could use something like this wrapper around Jim's code by Zoom Information which greatly simplifies that:

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

License

Aviad's solution is BSD.

Jim's solution is public domain, and so is its wrapper mentioned above.

清晨说晚安 2024-08-23 12:53:37

现在 Java 有了 Lambda,这两种方法都可以变得更加简洁。您可以执行类似

public Yielderable<Integer> oneToFive() {
    return yield -> {
        for (int i = 1; i < 10; i++) {
            if (i == 6) yield.breaking();
            yield.returning(i);
        }
    };
}

我在这里解释了更多内容的操作。

Both of these approaches can be made a bit cleaner now Java has Lambdas. You can do something like

public Yielderable<Integer> oneToFive() {
    return yield -> {
        for (int i = 1; i < 10; i++) {
            if (i == 6) yield.breaking();
            yield.returning(i);
        }
    };
}

I explained a bit more here.

指尖上得阳光 2024-08-23 12:53:37

我知道这是一个非常老的问题,上面描述了两种方法:

  • 移植时字节码操作并不那么容易;
  • 基于线程的yield显然会产生资源成本。

然而,还有另一种(第三种,可能是最自然的)方法在 Java 中实现 yield 生成器,它是最接近 C# 2.0+ 编译器对 yield return/break< 的实现。 /code> 生成:lombok-pg。它完全基于状态机,需要与 javac 紧密配合来操作源代码 AST。不幸的是,lombok-pg 支持似乎已停止(超过一两年没有存储库活动),并且原始的 Project Lombok 遗憾的是缺少 yield 功能(不过它有更好的 IDE,如 Eclipse、IntelliJ IDEA 支持)。

I know it's a very old question here, and there are two ways described above:

  • bytecode manipulation that's not that easy while porting;
  • thread-based yield that obviously has resource costs.

However, there is another, the third and probably the most natural, way of implementing the yield generator in Java that is the closest implementation to what C# 2.0+ compilers do for yield return/break generation: lombok-pg. It's fully based on a state machine, and requires tight cooperation with javac to manipulate the source code AST. Unfortunately, the lombok-pg support seems to be discontinued (no repository activity for more than a year or two), and the original Project Lombok unfortunately lacks the yield feature (it has better IDE like Eclipse, IntelliJ IDEA support, though).

£烟消云散 2024-08-23 12:53:37

我刚刚发布了另一个(麻省理工学院许可的)解决方案此处,它在单独的线程中启动生产者,并且在生产者和消费者之间设置一个有界队列,允许生产者和消费者之间进行缓冲、流量控制和并行流水线(以便消费者可以在生产者致力于生产下一个项目时消耗前一个项目) 。

您可以使用这个匿名内部类形式:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

例如:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5) {
    @Override
    public void producer() {
        for (int i = 0; i < 20; i++) {
            System.out.println("Producing " + i);
            produce(i);
        }
        System.out.println("Producer exiting");
    }
}) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}

或者您可以使用 lambda 表示法来减少样板代码:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5, producer -> {
    for (int i = 0; i < 20; i++) {
        System.out.println("Producing " + i);
        producer.produce(i);
    }
    System.out.println("Producer exiting");
})) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}

I just published another (MIT-licensed) solution here, which launches the producer in a separate thread, and sets up a bounded queue between the producer and the consumer, allowing for buffering, flow control, and parallel pipelining between producer and consumer (so that the consumer can be working on consuming the previous item while the producer is working on producing the next item).

You can use this anonymous inner class form:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

for example:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5) {
    @Override
    public void producer() {
        for (int i = 0; i < 20; i++) {
            System.out.println("Producing " + i);
            produce(i);
        }
        System.out.println("Producer exiting");
    }
}) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}

Or you can use lambda notation to cut down on boilerplate:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5, producer -> {
    for (int i = 0; i < 20; i++) {
        System.out.println("Producing " + i);
        producer.produce(i);
    }
    System.out.println("Producer exiting");
})) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}
奶气 2024-08-23 12:53:37

Stream.iterate(seed, SeedOperator).limit(n).foreach(action) 与yield 运算符不同,但以这种方式编写自己的生成器可能很有用:

import java.util.stream.Stream;
public class Test01 {
    private static void myFoo(int someVar){
        //do some work
        System.out.println(someVar);
    }
    private static void myFoo2(){
        //do some work
        System.out.println("some work");
    }
    public static void main(String[] args) {
        Stream.iterate(1, x -> x + 1).limit(15).forEach(Test01::myFoo);     //var1
        Stream.iterate(1, x -> x + 1).limit(10).forEach(item -> myFoo2());  //var2
    }
}

Stream.iterate(seed, seedOperator).limit(n).foreach(action) is not the same as yield operator, but it may be usefull to write your own generators this way:

import java.util.stream.Stream;
public class Test01 {
    private static void myFoo(int someVar){
        //do some work
        System.out.println(someVar);
    }
    private static void myFoo2(){
        //do some work
        System.out.println("some work");
    }
    public static void main(String[] args) {
        Stream.iterate(1, x -> x + 1).limit(15).forEach(Test01::myFoo);     //var1
        Stream.iterate(1, x -> x + 1).limit(10).forEach(item -> myFoo2());  //var2
    }
}
仅一夜美梦 2024-08-23 12:53:37

我还建议您是否已经在项目中使用 RXJava 将 Observable 用作“屈服者”。如果您制作自己的 Observable,则可以以类似的方式使用它。

public class Example extends Observable<String> {

    public static void main(String[] args) {
        new Example().blockingSubscribe(System.out::println); // "a", "b", "c", "d"
    }

    @Override
    protected void subscribeActual(Observer<? super String> observer) {
        observer.onNext("a"); // yield
        observer.onNext("b"); // yield
        observer.onNext("c"); // yield
        observer.onNext("d"); // yield
        observer.onComplete(); // finish
    }
}

可观察量可以转换为迭代器,因此您甚至可以在更传统的 for 循环中使用它们。 RXJava 还为您提供了非常强大的工具,但如果您只需要一些简单的东西,那么这可能有点过大了。

I'd also suggest if you're already using RXJava in your project to use an Observable as a "yielder". It can be used in a similar fashion if you make your own Observable.

public class Example extends Observable<String> {

    public static void main(String[] args) {
        new Example().blockingSubscribe(System.out::println); // "a", "b", "c", "d"
    }

    @Override
    protected void subscribeActual(Observer<? super String> observer) {
        observer.onNext("a"); // yield
        observer.onNext("b"); // yield
        observer.onNext("c"); // yield
        observer.onNext("d"); // yield
        observer.onComplete(); // finish
    }
}

Observables can be transformed into iterators so you can even use them in more traditional for loops. Also RXJava gives you really powerful tools, but if you only need something simple then maybe this would be an overkill.

沩ん囻菔务 2024-08-23 12:53:37
// Java code for Stream.generate()
// to generate an infinite sequential
// unordered stream
import java.util.*;
import java.util.stream.Stream;
  
class GFG {
      
    // Driver code
    public static void main(String[] args) {
      
    // using Stream.generate() method 
    // to generate 5 random Integer values
    Stream.generate(new Random()::nextInt)
      .limit(5).forEach(System.out::println); 
    }
}

从这里

// Java code for Stream.generate()
// to generate an infinite sequential
// unordered stream
import java.util.*;
import java.util.stream.Stream;
  
class GFG {
      
    // Driver code
    public static void main(String[] args) {
      
    // using Stream.generate() method 
    // to generate 5 random Integer values
    Stream.generate(new Random()::nextInt)
      .limit(5).forEach(System.out::println); 
    }
}

From here.

倾城花音 2024-08-23 12:53:37

我编写了一个新的 ,它实现了 Java 生成器。它简单、无线程且快速。

下面是生成无限斐波那契数的示例:

public static Seq<Integer> fibonacci() {
    return c -> {
        int a = 1;
        int b = 1;
        c.accept(a);
        c.accept(b);
        while (true) {
            c.accept(b = a + (a = b));
        }
    };
}

Seq 接口就像 Java Stream 和 Kotlin Sequence 一样,但比它们都快。

在这里,让我们打印斐波那契数列的前 7 个元素

Seq<Integer> fib = fibonacci();
fib.take(7).printAll(","); // => 1,1,2,3,5,8,13

对于最初的问题,生成树的所有节点?一根线就够了。

Seq<Node> seq = Seq.ofTree(root, n -> Seq.of(n.left, n.right));

I wrote a new library that has implemented generator for Java. It's simple, thread-free and fast.

Here is an example of generating endless fibonacci numbers:

public static Seq<Integer> fibonacci() {
    return c -> {
        int a = 1;
        int b = 1;
        c.accept(a);
        c.accept(b);
        while (true) {
            c.accept(b = a + (a = b));
        }
    };
}

The Seq interface is just like Java Stream and Kotlin Sequence, but faster than all of them.

Here, let's print the first 7 elements of the fibonacci series

Seq<Integer> fib = fibonacci();
fib.take(7).printAll(","); // => 1,1,2,3,5,8,13

For the original problem, yielding all nodes of a tree? One line is enough.

Seq<Node> seq = Seq.ofTree(root, n -> Seq.of(n.left, n.right));
青芜 2024-08-23 12:53:37

是的,你可以在 Java 中使用类似 Yield Return 的语法!

在java中,要获得这样的语法:

Yielder(list).forEach(x->doSomething(x))

创建一个这样的类:

public class Yielder {
    public List<String> l;
    
    public Yielder(List<String> l){
       this.l=l;
    }

    public void forEach(Consumer<String> f){
        for(String s : l){
            f.accept(s);
        }
    }
}

其中f.accept() 履行yield return 的作用。

Yes, you can have yield return like syntax in Java!

In java, to gain syntax like:

Yielder(list).forEach(x->doSomething(x))

One creates a class like this:

public class Yielder {
    public List<String> l;
    
    public Yielder(List<String> l){
       this.l=l;
    }

    public void forEach(Consumer<String> f){
        for(String s : l){
            f.accept(s);
        }
    }
}

Where f.accept() fulfills the role of yield return.

不语却知心 2024-08-23 12:53:37

下面介绍了如何将具有 Yield 逻辑的代码转换为 Java 等效代码。它可能需要一些努力,但至少不需要第三方库(仍然需要对 lambda 的支持):

C# :

public IEnumerable<int> Foo()
{
     for (int i = 0; i < 5; i++)
     { 
          yield return i;
     }
}

Java :

public static Iterable<Integer> foo() {
    return () -> new Iterator<>() {
        Integer counter = 0;

        @Override
        public boolean hasNext() {
            return counter < 5;
        }
        
        @Override
        public Integer next() {
            return counter++;
        }
    };
}

Here is how to convert code with yield logic to Java equivalent. It might require some effort but at least there is no need of a third party library (still, it require support for lambdas):

C# :

public IEnumerable<int> Foo()
{
     for (int i = 0; i < 5; i++)
     { 
          yield return i;
     }
}

Java :

public static Iterable<Integer> foo() {
    return () -> new Iterator<>() {
        Integer counter = 0;

        @Override
        public boolean hasNext() {
            return counter < 5;
        }
        
        @Override
        public Integer next() {
            return counter++;
        }
    };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文