代码之家  ›  专栏  ›  技术社区  ›  Margus

Java作为通用的高阶函数式编程语言

  •  1
  • Margus  · 技术社区  · 16 年前

    我正在解决算法问题,我想编写可应用于集合的自定义函数和谓词。最近,我开始使用Google Collections,它非常适合这个任务。

    如果不清楚我想做什么,下面是一个例子:

    I.range(1,999).multiplication(I.range(1,999)).palindromes().max().echo(2); 
    
    1. 返回序列1:999(x2)的集合
    2. 使用方法times()transform为每个项返回2个集合
    3. 返回通过回文过滤器的每个项目的集合
    4. 返回最大元素E<&燃气轮机;共3人。后果
    5. 返回元素E<&燃气轮机;并调用方法toString和基数2(二进制)并将其打印到屏幕上

    E类定义为:

    public class E<T extends Number & Comparable<? super T>> extends Number implements Comparable<E<T>> {//...
    

    C类定义为:

    public class C<T extends E<NC>, NC extends Number & Comparable<? super NC>> implements Collection<T> {
    

     public Collection<T> multiplication(T value) {
      return Collections2.transform(this, new Function<T, T>() {
       @Override
       public T apply(T in) {
        return in.times(value);
       }
      });
     }
    

    我在E类之前使用过以下代码

     /** Multiplies 2 numbers */
     public E<?> times(E<?> elem) {
      if (this.value == null || elem.value == null) return E.Null();
      if (this.value instanceof Integer) {
       return E.with(I, this.intValue() * elem.intValue());
      } else if (this.value instanceof Long) {
       return E.with(L, this.longValue() * elem.longValue());
      } else if (this.value instanceof Float) {
       return E.with(F, this.floatValue() * elem.floatValue());
      } else if (this.value instanceof Double) {
       return E.with(D, this.doubleValue() * elem.doubleValue());
      } else if (this.value instanceof BigInteger) {
       return E.with(BI, this.BigIntegerValue().multiply(
        elem.BigIntegerValue()));
      } else if (this.value instanceof BigDecimal) { return E.with(BD,
       this.BigDecimalValue().multiply(elem.BigDecimalValue())); }
    
      return E.Null();
     }
    

    我应该改变什么,以使编写自定义函数和谓词所需的时间最少 'suckiness' .

    编辑:

    A井,将C改为:

    public class C<T extends E<?>> extends ArrayList<T> {
    

    只是抑制了一般的通配符转换警告,比如

    public Collection<T> multiplication(Collection<T> value) {
        C<T> result = new C<T>();
    
        for (T t : value)
            result.addAll(multiplication(t));
        return result;
    }
    
    public Collection<T> multiplication(final T value) {
        return Collections2.transform(this, new Function<T, T>() {
            @SuppressWarnings("unchecked")
            @Override
            public T apply(T in) {
                return (T) in.times(value);
            }
        });
    }
    

    因此,如果类型匹配,这是可行的。

    6 回复  |  直到 16 年前
        1
  •  3
  •   James Black    16 年前

    你能用吗 Scala ?

        2
  •  3
  •   Apocalisp    16 年前

    你可以使用 Functional Java library.

    package euler;
    
    import fj.F;
    import static fj.Function.flip;
    import fj.data.Stream;
    import static fj.data.Stream.range;
    import static fj.function.Integers.multiply;
    import static fj.function.Integers.add;
    import static fj.pre.Equal.charEqual;
    import static fj.pre.Equal.streamEqual;
    import static fj.pre.Ord.intOrd;
    import static fj.pre.Show.intShow;
    
    /**
     * Find the largest palindrome made from the product of two 3-digit numbers.
     */
    public class Problem4
      {private static final F<Integer, Boolean> palindrome =
        new F<Integer, Boolean>() {public Boolean f(final Integer i)
          {final Stream<Character> s = intShow.show(i);
           return streamEqual(charEqual).eq(s.reverse(), s);}}
    
       public static void main(final String[] a)
         {final Stream<Integer> xs = range(100, 999);
          intShow.println(xs.tails().bind(xs.zipWith(multiply)).filter(palindrome)
                          .foldLeft1(intOrd.max));}}
    

    下面是它的外观 noise-filtering glasses

    palindrome i = s == reverse s
      where s = show i
    
    main = putStrLn . maximum . filter palindrome $ tails xs >>= zipWith (*) xs
      where xs = [100..999]
    
        3
  •  2
  •   Yishai    16 年前

    虽然我同意这样的评论,即这不太符合Java的风格,但实现这一点的一种方法可能是初始化以下映射:

      public interface Multiply<T> {
            T multiply(T one, T two);
      }
    
    
       //In some initializaiton code, say a static initializer
    
       Map<Class<?>, Multiply<?>> map = newHashMap(); //That is the method from Google Collections
       map.put(Integer.class, new Multiply<Integer>(){
            Integer multiply(Integer one, Integer two) {
                return one * two;
            }
       });
    

    等等,每种情况。然后在代码中(使用适当的空检查):

      Multiply mult = map.get(this.value.getClass());
      Object val = mult.multiply(this.value, elem.value));
    

    注意,这里的原始类型是故意的,我必须考虑是否可以做到这一点,并保持一切通用性。无论如何,不容易。

    但是给定val类,您可以检索适当的E。

    不是,这完全是我的想法,所以我还没有测试一些潜在的通用通病。

        4
  •  1
  •   Andrei Vajna II    16 年前

    你可以使用反射。根据类的名称,找到方法并调用它。例如,对于“BigInteger”,您称之为“BigIntegerValue”,等等。

    在java中处理不同的原语类型是非常烦人的,我不确定这是否容易做到。在你做对之前,你可能会失败几次。也许您可以创建一个通用的数字类,它可以抽象出大小(如int、long、short等)和类型(integer、decimal等)之间的差异,如 凉的

    另一种方法是,您可以编写一个代码生成器来为您编写所有那些烦人的案例。它不应该太复杂。

        5
  •  1
  •   Mario Fusco    16 年前

    我相信Scala可能是解决您问题的最佳方案。

    相反,如果您不得不使用Java,还可以看看lambdaj http://code.google.com/p/lambdaj/

    你会发现你所需要的大部分东西已经在那里实现了。

        6
  •  0
  •   ddyer    16 年前

    Clojure是一种在jvm上运行的类似lisp的函数式语言。它有 使用传统的无类型lisp语义的bignums、ratio和bigdecimal。