【问题标题】:Yield Return In JavaJava中的收益回报
【发布时间】: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 功能。

【问题讨论】:

标签: c# java linked-list


【解决方案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】:

      试试这个

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

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

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

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

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

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

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

        【讨论】:

          【解决方案6】:

          一个yield返回操作可视为

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

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

          1. 添加一些机制来实现 goto 语法:addInstruction(..) 和 jmp()
          2. 并将状态/变量存储在某处:setVariable(name,value)、yield(value)
          3. 一种暂时挂起/恢复的方法: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 将协程转换为迭代器

          【讨论】:

          【解决方案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

              【讨论】:

              • 该页面为德语,您的答案为英语。请考虑添加您的库如何工作的代码示例。
              猜你喜欢
              • 2011-05-27
              • 2012-01-14
              • 1970-01-01
              • 2012-08-02
              • 2018-01-23
              • 2010-12-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多