【问题标题】:making a lazy iterator for an arithmetic series?为算术级数制作一个惰性迭代器?
【发布时间】:2011-04-26 20:17:52
【问题描述】:

这是我为算术级数编写的 implements Iterable<Integer> 的课程(从 startstop,步长为 step

package com.example.test;

import java.util.Iterator;
import com.google.common.collect.AbstractIterator;

public class ArithmeticSeries implements Iterable<Integer>
{
    final private int start, step, stop;
    public int getStart() { return this.start; } 
    public int getStep() { return this.step; } 
    public int getStop() { return this.stop; }

    public ArithmeticSeries(int start, int step, int stop)
    {
        this.start = start;
        this.step = step;
        this.stop = stop;
    }
    @Override public Iterator<Integer> iterator()
    {
        return new AbstractIterator<Integer>() {
            private Integer n = null;
            @Override protected Integer computeNext() {
                int next;
                if (this.n == null)
                {
                    next = getStart(); 
                }
                else
                {
                    next = this.n + getStep();
                    if ((getStep() > 0 && next > getStop()) 
                     || (getStep() < 0 && next < getStop()))
                        return endOfData();
                }
                this.n = next;
                return next;
            }
        };
    }
    @Override public String toString() {
        return getStart()+":"+getStep()+":"+getStop();
    }

    public static void main(String[] args) {
        Iterable<Integer> range = new ArithmeticSeries(100,-1,80);
        System.out.println(range);
        for (int i : range)
            System.out.println(i);
    }
}

有没有更优雅的实现iterator() 的方法?我不喜欢 Integer 的空值检查和使用(替代方案是一个额外的标志 boolean firstTime),这似乎是错误的。

【问题讨论】:

    标签: java iterator guava


    【解决方案1】:
    return new AbstractIterator<Integer>() {
      int next = getStart();
    
      @Override protected Integer computeNext() {
        if (isBeyondEnd(next)) {
          return endOfData();
        }
        Integer result = next;
        next = next + getStep();
        return result;
      }
    };
    

    如果您愿意,您可以将其实现为不可变的List&lt;Integer&gt;。如果您扩展AbstractList,那么Iterator 将为您处理。实际上,我认为AbstractList 真的是最好的选择。整个班级看起来像这样(我还没有检查过它在所有情况下都能正常工作):

    public class ArithmeticSeries extends AbstractList<Integer> {
      private final int start;
      private final int step;
      private final int size;
    
      public ArithmeticSeries(int start, int end, int step) {
        this.start = start;
        this.step = (start < end) ? step : -step;
        this.size = (end - start) / this.step + 1;
      }
    
      @Override public Integer get(int index) {
        return start + step * index;
      }
    
      @Override public int size() {
        return size;
      }
    }
    

    【讨论】:

    • 嗯。不知道为什么我没有想到这一点。我仍然必须防止溢出,但这与我的原始代码相同。
    【解决方案2】:

    您可以使用 Function 来抽象连续的值,并使用 Predicate 来控制迭代的结束,最终创建一个 Unfold 实现:

    public final class UnfoldIterator<E> implements Iterator<E> {
        public static <E> Iterator<E> unfold(E initial, Function<? super E, ? extends E> next, Predicate<? super E> finished) {
            return new UnfoldIterator<E>(initial, next, finished)
        }
        private final Function<? super E, ? extends E> next;
        private final Predicate<? super E> finished;
        private E element;
    
        public UnfoldIterator(E initial, Function<? super E, ? extends E> next, Predicate<? super E> finished) {
            super();
            this.next = next;
            this.finished = finished;
            this.element = initial;
        }
        @Override protected Integer computeNext() {
            if (finished.apply(element)) {
                return endOfData();
            }
            E result = element;
            element = next.apply(element);
            return result;
        }
    }
    

    那么 ArithmeticSeries 变为:

    public Iterable<Integer> series(final int start, final int step, final int stop) {
        return new Iterable<Integer>() {
            public Iterator<Integer> iterator() {
                return new UnfoldIterator<Integer>(start, new Function<Integer, Integer>() {
                    public Integer apply(Integer from) {
                        return from - step;
                    }
                }, new Predicate<Integer>() {
                    public boolean apply(Integer input) {
                        return input >= stop;
                    }
                });
            }
        };
    }
    

    当然,现在代码看起来更复杂了,但是通过适当的基函数进行比较和代数,调用变得更加清晰:

    return unfold(start, subtractBy(step), not(lessThan(stop)));
    

    【讨论】:

    • +1 用于建议功能性替代方案。但是,它对我们团队的可维护性太难了。我们只是不这么认为......
    【解决方案3】:

    我认为解决您的番石榴问题的最佳工具是AbstractLinkedIterator。您的示例的实现如下所示:

    final Iterator<Integer> series = new AbstractLinkedIterator<Integer>(100) {
    
        @Override protected Integer computeNext(final Integer previous) {
            return previous == 80 ? null : previous - 1;
        }
    };
    while (series.hasNext()) {
        System.out.println(series.next());
    }
    

    您可以轻松地为此迭代器创建一个Iterable 适配器,例如像这样:

    package sk.the0retico.guava;
    
    import java.util.Iterator;
    
    import com.google.common.base.Function;
    import com.google.common.collect.AbstractLinkedIterator;
    
    public class LinkedIterable<T> implements Iterable<T> {
    
        public static final <T> Iterable<T> from(final T first,
            final Function<T, T> computeNext) {
            return new LinkedIterable<T>(first, computeNext);
        }
    
        public static void main(final String[] args) {
            final Iterable<Integer> series = LinkedIterable.from(100,
                                                 new Function<Integer, Integer>() {
    
                @Override public Integer apply(final Integer input) {
                    return input == 80 ? null : input - 1;
                }
            });
            for (final Integer value : series) {
                System.out.println(value);
            }
        }
    
        private final Function<T, T> computeNext;
    
        private final T first;
    
        public LinkedIterable(final T first, final Function<T, T> computeNext) {
            this.first = first;
            this.computeNext = computeNext;
        }
    
        @Override public Iterator<T> iterator() {
            return new AbstractLinkedIterator<T>(first) {
    
                @Override protected T computeNext(final T previous) {
                    return computeNext.apply(previous);
                }
            };
        }
    }
    

    但是,这种方法对返回 null 的提供函数有特殊限制。

    【讨论】:

    • +1 表示建议 - 用于制作无限列表,但不适用于有限列表(通过解耦 computeNext 函数,您消除了调用 endOfData() 的可能性)
    • @Jason S:该示例演示了如何包装LazyList,因此不需要endOfData() 来获得有限的Iterable
    • @Jason S: ...根据 javadocs,当没有更多元素跟随时,您的函数可以返回 null
    • @Gabriel:嗯——你说得有道理。我不熟悉 AbstractLinkedIterator,只是 AbstractIterator(你必须调用 endOfData())。不过,您的示例并未显示有限列表。
    • (或者更确切地说,有限性是通过在系列本身外部使用 Iterables.limit() 来实现的。)
    猜你喜欢
    • 1970-01-01
    • 2011-01-16
    • 2012-07-02
    • 2016-04-26
    • 2012-06-28
    • 2012-09-20
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    相关资源
    最近更新 更多