如何实现`range()`函数

3

介绍

如何在Java 6中编写公共静态 range方法Range类,以至少覆盖其他编程语言中实现的常见功能?

如果您想回答这个问题,那么您可以忽略以下内容。


关于

我时不时地会错过其他语言具有的功能。我对自己编写的任何语言都有不同的编码风格,而且我不愿意改变这个习惯。因此,如果我想重用在不同语言中编写的算法,则必须进行烦人的小技巧或解决方法,以涵盖缺失的功能。我想找到永久有效和高效的range()修复方法。

对我来说- range()方法以可能是懒惰的方式返回输入范围,并具有默认值。在任何时候,它都有一个开始,结束条件和获取下一个元素的方法。它应该在for each循环内外完美工作。

备注

我真的希望不使用任何外部库,除了Google Guava或等效库。通过等效库,我指的是经过适当测试,与JDK完美配合且不被视为已弃用的代码。

alt text

可能不太清楚,因此:

  • 通过如何编写,我指-您不必编写它,只需描述常见陷阱以及推荐方法的优缺点。
  • 这是循环代码,但也可以使用
  • 这不是作业:P

除了整数输入是最常用的功能外,我真的希望它能够很好地与类实例(如BigInteger和Joda DateTime)一起使用。

想法

  • 必须能够在for-each循环中使用
  • 通用,但类型安全
  • 可以使用预先存在的容器方法,例如:.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 {

基于个人偏好,我使用 Builder 模式实现了 Range

编辑

Range 的实现在各个语言中存在差异,包括ScalaPython 3Groovy.Net(使用 linq)[c#,f#,vb 和 c ++] rubyPHP等。

这里我也可以添加一个示例,展示我打算如何改进(simple sample case)。

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();}
    };
}

这可能不是你想要的,但它很接近... http://snippets.dzone.com/posts/show/3792 - st0le
尽管您已经描述了,但我仍然对您在range中想要的内容进行了假设。如果您能指出特定语言和/或指向另一种语言/库的range关键字/类/方法/API的文档,那将会很有帮助。 - Bert F
2个回答

1

痛点可能在于需要为每种类型编写函数:

  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,您将不得不为每种类型重新实现此功能(BigIntegerlong等)。

我会质疑使用 <T extends Comparable<T>> 而不是只使用 <T> 的必要性。我认为强制这种限制没有任何优势。

与集合不同,Iterable 类型不强制执行 equality contract。如果您想要能够比较范围而不需要外部迭代其所有元素,则最好返回定义了这样一个契约的 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循环相比,在大范围内这可能会显着地不够高效。


我想不出任何数据结构,不应该有compareTo方法并且不受“范围”限制。而且我不明白为什么我必须首先强制实现相等性协定。痛点在于确保代码按预期工作,而不是编写代码。此外,我可以存储默认映射或i + _step的枚举,但通常会更改该部分,因此我不认为存在问题。Java没有通用原始类型选项,我认为通过使用BigInteger来表达我不关心这一点。 - Margus

0

我会选择最近的路径。创建一个类似于这样的接口:

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()函数,以便于使用。

我选择这种方法是因为它使得添加更多可步进类型变得相当简单。至于缺点,这种方法有些冗长。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接