Java中的收益回报

Posted

技术标签:

【中文标题】Java中的收益回报【英文标题】:Yield Return In Java 【发布时间】:2011-01-22 02:00:43 【问题描述】:

我在 java 中使用泛型创建了一个链表,现在我希望能够遍历列表中的所有元素。在 C# 中,我会在链接列表中使用yield return,同时遍历列表中包含的元素列表。

我将如何创建上述的 java 版本,以便可以迭代链接列表中包含的所有项目?

我希望能够编写代码 ala

LinkedList<something> authors = new LinkedList<something>();
for (Iterator<something> i = authors.Values ; i.HasNext())
      doSomethingWith(i.Value);

并且认为值“属性”/方法将包含类似的代码

LinkedListObject<something> current = first;
While (current != null)
 yield return current.getValue();
 current = current.getNext()

编辑:请注意,我对使用任何 3rd 方 API 不感兴趣。仅内置 java 功能。

【问题讨论】:

***.com/questions/1980953/… 我不懂 C#。好奇,收益回报有什么作用? 检查这个:msdn.microsoft.com/en-us/library/9k7k7cf0(VS.80).aspx 过多以编译器为中心?如果我想我必须自己编写编译器为我编写的所有东西...... @MerlynMorgan-Graham 或者当计算(生成器函数)很昂贵并且需要惰性评估时。 【参考方案1】:

您可以返回 Iterable 的匿名实现。效果非常相似,只是这更加冗长。

public Iterable<String> getStuff() 
    return new Iterable<String>() 

        @Override
        public Iterator<String> iterator() 
            return new Iterator<String>() 

                @Override
                public boolean hasNext() 
                    // TODO code to check next
                

                @Override
                public String next() 
                    // TODO code to go to next
                

                @Override
                public void remove() 
                    // TODO code to remove item or throw exception
                

            ;
        
    ;

【讨论】:

【参考方案2】:

“yield return”是一个非常复杂的编译器技巧。它基本上可以让您以声明方式实现 IEnumerable,而无需“弄清楚”如何构建迭代器的任何烦人的细节。不幸的是,它不能很好地翻译成其他语言,因为很少有编译器有这样的能力。在某些方面,“收益回报”与革命性一样可怕。

基本上在 C# 中,编译器会生成 IEnumerable 和 IEnumerator (of T) 的两个实现。它通过基本上将您的“方法”的局部变量实现为生成的实现类中的实例字段以及检查包含“yield return”工件的帧来做到这一点。一旦你知道了这一点,一个全面的开发人员应该可以明确地完成同样的事情......虽然不是那么简洁。为了演示,我将 CONCAT!

public static <T> Iterable<T> concat(Iterable<T> x, Iterable<T> y)

    for(T e: x)
    
        yield return e;
    

    for(T e: y)
    
        yield return e;
    


// becomes ....

public static <E> Iterator<E> concat_(Iterable<E> x, Iterator<E> y)

    T e1, e2;
    Iterator<E> i1, i2;

    Iterator<E> s;
    Iterator<E> s4 = new Iterator<E>()
    
        public bool hasNext()
        
            return false;
        

        public E next()
        
            throw ... ;
        

        public void remove()
        
            throw ... ;
        
    

    Iterator<E> s3 = new Iterator<E>()
    
        Iterator<E> act()
        
            if(i2.hasNext())
            
                return i2;
            

            i2 = y.iterator();
            return (s = s4);
        

        public bool hasNext()
        
            return act().hasNext();
        

        public E next()
        
            return act().next();
        

        public void remove()
        
            return i2.remove();
        
    

    Iterator<E> s2 = new Iterator<E>()
    
        Iterator<E> act()
        
            if(i1.hasNext())
            
                return i1;
            

            i2 = y.iterator();
            return (s = s3);
        

        public bool hasNext()
        
            return act().hasNext();
        

        public E next()
        
            return act().next();
        

        public void remove()
        
            return i1.remove();
        
    ;

    Iterator<E> s1 = new Iterator<E>()
    
        Iterator<E> act()
        
            i1 = x.iterator();
            return s = s2;
        

        public bool hasNext()
        
            return act().hasNext();
        

        public E next()
        
            return act().next();
        

        public void remove()
        
            return act().remove();
        
    ;

    s = s1;
    return new Iterator<T>()
    
        public bool hasNext()
        
            return s.hasNext();
        

        public E next()
        
            return s.next();
        

        public void remove()
        
            return s.remove();
        
    ;


public static <T> Iterable<T> concat(Iterable<T> x, Iterable<T> y)

    return new Iterable<T>()
    
        public Iterator<T> iterator()
        
            return concat_(x, y)
        
    ;


// tada!

如果你们都原谅我凌晨 3 点的伪 java...

【讨论】:

【参考方案3】:

试试这个

com.infomancers.collections.yield

也可以查看这篇文章以获取示例实现:

Implementation details for Java Yielder

【讨论】:

【参考方案4】:

我不明白为什么人们在谈论线程......关于收益回报有什么我不知道的吗?

据我了解,yield return 只是保存方法堆栈并在以后恢复它。要实现收益返回,您只需手动保存状态。有关详细信息,请参阅 Java 迭代器类,但对于链接列表,您可以直接保存当前项目。对于数组,您只需要索引。

【讨论】:

这是正确的。 Yield 和 yield return 不使用 C# 中的线程。他们进行编译时转换并创建状态机,但该状态机不使用任何额外的线程(尽管它可能是线程安全的)。【参考方案5】:

只是为了帮助读者理解小细节。

如果您创建一个包含所有结果元素的新列表并返回该列表,那么这是一个很好的实现,代码足够简单。您可以根据需要拥有任何有趣的数据结构,并且在扫描它以查找正确的条目时,只需返回所有匹配项的列表,您的客户端将在列表上进行迭代。

如果你想保存一个状态,它可能会更复杂。每次调用函数时,您都需要到达您所在的位置。更不用说重入问题等了。

带有线程的解决方案不会创建新列表。它就像第一个解决方案一样简单。唯一的问题是你涉及到一个线程同步,这有点难以编码,并且有性能损失。

所以,是的,yield return 很棒,Java 中没有。不过也有解决方法。

【讨论】:

【参考方案6】:

一个yield返回操作可视为

    在那里放置一些检查点 在某处写一个值 获取简历后,跳转到旁边的指令。

因此,我将它实现为类似状态机的类,协程。 在这种机制中,每条指令都有其指令指针、索引和 指令可能带有标签,因此我们可以使用 jmp(label) 跳转到标签。

    添加一些机制来实现 goto 语法:addInstruction(..) 和 jmp() 并将状态/变量存储在某处:setVariable(name,value)、yield(value) 一种暂时挂起/恢复的方法:exec()

例如:

    public class FibbonaciCoroutine implements Iterator<BigInteger> 
        BigInteger[] bucket =  new BigInteger("1"), new BigInteger("1"), new BigInteger("0") ;
        int idx = 2;
        Coroutine coroutine = new Coroutine((pthis) -> 
    
            pthis.addInstruction("_label1", (me) -> 
                int p1 = idx - 2;
                int p2 = idx - 1;
                if (p1 < 0)
                    p1 += 3;
                if (p2 < 0)
                    p2 += 3;
                bucket[idx] = bucket[p1].add(bucket[p2]);
                idx = (idx + 1) % bucket.length;
    
                me.yield(bucket[idx]);
    
            );
            // goto
            pthis.addInstruction((me) -> 
                me.jmp("_label1");
            );
            pthis.start();
        );
    
        @Override
        public boolean hasNext() 
            return !coroutine.isStopped();
        
    
        @Override
        public BigInteger next() 
            while (coroutine.exec())
                ;
            return coroutine.getYieldValue();
        
    
        public static void main(String[] argv) 
            FibbonaciCoroutine cor = new FibbonaciCoroutine();
            for (int i = 0; i < 100 && cor.hasNext(); ++i) 
                System.out.printf("%d ", cor.next());
            
        
    
    

见FibonacciCoroutine.java

回到你的问题...

    LinkedListObject<something> current = first;
    While (current != null)
       yield return current.getValue();
       current = current.getNext()
    

可以转换成下面的代码

   //some where in class, or use Var<> to wrap it.
   Var<LinkedListObject<something> > current = new Var<>(first);
   Coroutine cor = new Coroutine();
   cor.While((ins)->current.get() != null).run((ins)->
       ins.addInstruction((c)->c.yield(current.get().getValue()) );
       // wrap it with lambda for being a checkpoint
       ins.addInstruction( (c)->current.set(current.get().getNext()) );
   );

所以我们可以使用它的 getYieldValue() 来检索结果,或者简单地调用 coroutine.iterator 将协程转换为迭代器

【讨论】:

我也实现了一个递归例子BinaryTreeCoroutine【参考方案7】:

自发布此问题以来已经有很长时间了,我很不确定是否要为这么老的问题写答案,但是我想到了另一种实现此目标的方法,我想在这里展示它以防它对任何人有所帮助搜索这个,因为这个 SO 线程是 Google 中最早的热门话题之一。

下面显示的代码已在我的脑海中编译。绝对不能保证它是正确的,但它背后的想法是。

使用回调

是的,我知道,它与yield return 不同。但我不认为 OP 特别想要一个替代品,可以(用适量的糖)放入for (var x : &lt;some_iterator&gt;)。相反,我的方法更类似于 C# 的 linq(或 Java 的 stream()),而不是 yielded 返回。

@FunctionalInterface
public interface Looper<T> 
    void each(T item);




public interface Loopable<T> 
    void forEach(Looper<? super T> looper);

然后你将在你的代码中实现Loopable&lt;T&gt;,创建这个伪迭代器。其实不是,它只是利用@FunctionalInterfaces,这是Java 的回调方式(sorta

public class WhatEvs implements Loopable<WhatEvs> 
    // ...
    @Override
    public void forEach(Looper<? super T> looper) 
        while(your_condition) 
            WhatEvs nextItem = getNextItem();
            looper.each(nextItem);
        
    

【讨论】:

【参考方案8】:

如果您想要yield return 的全部功能,您可能需要在两个线程中进行设置——一个用于第一种方法,一个用于第二种方法。然后第一个线程应该wait 直到第二个线程将它的值放在某个可访问的地方并且notifys 它已经准备好了。然后第一个线程将处理该值,wait 处理下一个值,等等。

【讨论】:

【参考方案9】:

使用我的java库实现yield return,不使用线程或字节码操作

http://www.heinerkuecker.de/YieldReturnForNested.html

【讨论】:

该页面为德语,您的答案为英语。请考虑添加您的库如何工作的代码示例。

以上是关于Java中的收益回报的主要内容,如果未能解决你的问题,请参考以下文章

收益回报与回报选择

R:将每日收益转换为每月收益

收益回报与回报 IEnumerable<T>

挖矿的收益到底怎样,年回报率能达到500%​?

用ML算法预测股票收益率

2021年当前市场S19系列挖矿投资收益分析(年回报率超90%))