сделать ленивый итератор для арифметического ряда? - PullRequest
3 голосов
/ 27 апреля 2011

Вот класс, который я написал, implements Iterable<Integer> для арифметической серии (от start до stop с шагом 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), это просто неправильно.

Ответы [ 3 ]

4 голосов
/ 27 апреля 2011
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<Integer>.Если вы продлите 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 голосов
/ 27 апреля 2011

Вы можете использовать функцию для абстрагирования последовательных значений и предиката для управления окончанием итерации, в конечном итоге создавая реализацию Развернуть :

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 голос
/ 27 апреля 2011

Я думаю, что лучшим инструментом для вашей проблемы в гуаве является 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.

...