Как реализовать `range ()` - PullRequest
       5

Как реализовать `range ()`

3 голосов
/ 11 декабря 2010

Intro

Как написать общедоступную статическую метод диапазона или Класс диапазона в Java 6, чтобы он по крайней мере охватывал общие функциональные возможности, реализованные в других программахязыки?

Если вы хотите ответить на вопрос, то можете просто проигнорировать следующее.

О

Раз за разом я скучаю по функциям, которые есть у других языков.У меня разные стили написания для любого языка, на котором я пишу, и я не хочу менять эту привычку.Поэтому, если я хочу повторно использовать алгоритм, написанный на другом языке, я должен сделать небольшие раздражающие хаки или обходные пути, чтобы покрыть отсутствующую функциональность.Я хотел бы найти постоянное и эффективное исправление для range().

Для меня - range() метод возвращает диапазон ввода, возможно, ленивым способом и имеет значения по умолчанию.В любой момент у него есть начало, условие окончания и способ получить следующий элемент.Он должен прекрасно работать в для каждого цикла , а также вне его.

Примечания

Я бы действительно предпочел не использовать какую-либо внешнюю библиотеку, кроме Google Guava или аналогичной.,Под эквивалентом я подразумеваю код, который должным образом протестирован, отлично работает с JDK и не считается устаревшим.

alt text

Это может быть неясно, поэтому:

  • под How to write Я имею в виду - вам не нужно писать это, просто опишите общие подводные камни и каковы плюсы и минусы рекомендуемого подхода.
  • это loop code, но также может использоваться
  • это не домашнее задание: P

Помимо целочисленного ввода, это будет наиболее часто используемымМне бы очень хотелось, чтобы он хорошо работал с экземплярами типов классов, таких как BigInteger и Joda DateTime.

Идеи

  • должны быть в состоянии использовать в for-each loop
  • универсальный, но типизированный
  • способен использовать уже существующие методы контейнера, такие как: .cycle (), .filter (), .all (), .any (), transform ()

В качестве метода заголовок range() может выглядеть следующим образом:

/**TODO: Think about why / how to add step, limit, offset and cycle detection. */
public static <T extends Comparable<T>> Iterable<T> //
range(T from, Predicate<T> to, Function<T, T> fNext) //
        throws NullPointerException, IllegalArgumentException {

В качестве личного предпочтения я написал Range в качестве реализации шаблона Builder.

Редактировать

Диапазон реализации Scala , Python 3 , Groovy , .Net (с linq) [c #, f #, vb и c ++] , ruby ​​, PHP ... И все они отличаются.

Я мог бы также добавить пример fИз того, что я хочу сделать лучше ( простой пример ).

public static <T extends Comparable<T>> Iterable<T> //
range(T from, Predicate<T> to, Function<T, T> fNext) //
        throws NullPointerException {
    Preconditions.checkNotNull(from);
    Preconditions.checkNotNull(to);
    Preconditions.checkNotNull(fNext);

    T current = from;
    ArrayList<T> result = Lists.newArrayList();
    if (to.apply(current)) result.add(current);
    while (to.apply(current = Preconditions.checkNotNull(fNext.apply(current))))
        result.add(current);
    return result;
}

или ленивая альтернатива

//eats first element
public static <T extends Comparable<T>> Iterator<T> //
range2(final T from, final Predicate<T> to, final Function<T, T> fNext)
        throws NullPointerException, UnsupportedOperationException {
    Preconditions.checkNotNull(from);
    Preconditions.checkNotNull(to);
    Preconditions.checkNotNull(fNext);
    return new Iterator<T>() {
        T current = from;
        @Override public boolean hasNext() {return to.apply(current);}
        @Override public T next() {return current = Preconditions.checkNotNull(fNext.apply(current));}
        @Override public void remove() {throw new UnsupportedOperationException();}
    };
}

Ответы [ 2 ]

1 голос
/ 11 декабря 2010

Болевая точка, вероятно, должна будет написать функции для каждого типа:

  public static Function<Integer, Integer> intIncrementer(final int step) {
    class IntIncrementer implements Function<Integer, Integer> {
      private final int _step = step;

      @Override public Integer apply(Integer i) {
        return i + _step;
      }

      @Override public boolean equals(Object obj) {
        return (obj instanceof IntIncrementer) 
           && ((IntIncrementer) obj)._step == _step;
      }

      @Override public int hashCode() {
        return _step;
      }
    }

    return new IntIncrementer();
  }

Поскольку нет способа выразить i + _step в общем виде, вам придется переопределить это для каждого типа, который вы хотите поддерживать (BigInteger, long и т. Д.)

Я бы поставил под сомнение необходимость использования <T extends Comparable<T>> вместо <T>. Я не вижу никакого преимущества в наложении этого ограничения.

В отличие от коллекций, тип Итерируемый не налагает контракт на равенство . Может быть лучше вернуть тип Range, который определил такой контракт, если вы хотите иметь возможность сравнивать диапазоны без внешней итерации по всем их элементам.

public interface Range<T> extends Iterable<T> {
  // TODO: write the terms of the contract
  @Override public boolean equals(Object obj);
  @Override public int hashCode();
  // TODO: other useful methods?
}

Когда дело доходит до примитивов, в создании объектов-оболочек будут накладные расходы. Это может быть значительно менее эффективным в больших диапазонах по сравнению с обычным увеличением for.

0 голосов
/ 11 декабря 2010

Я бы выбрал ближайший путь.Создайте интерфейс, такой как этот:

interface Steppable<T>{
    T defaultStep(); //Returns 1 for most number types
    T value(); //Returns the value itself
    Steppable<T> step(T amount); //the stepping logic
}

Напишите целую кучу реализаций интерфейса для каждого типа, который я хочу использовать, внутри функции диапазона (целые, длинные, даты и т. Д.).Затем создайте перегруженную range() функцию для каждого степпируемого типа для простоты использования.

Я бы пошел на этот подход, потому что он делает добавление большего числа степпируемых типов довольно простым.Что касается недостатка, то этот подход несколько многословен.

...