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

时间:2009-09-14 19:21:25

标签: java generics functional-programming

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

我想以同样的方式使用BigInteger和BigDecimal,因为我会使用任何其他数字。在没有思考应该如何完成的情况下,我决定进行额外的抽象层(E类)。

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

I.range(1,999).multiplication(I.range(1,999)).palindromes().max().echo(2); 
  1. 返回序列1:999(x2)
  2. 的集合
  3. 使用方法times()转换
  4. 返回每个项目的2个集合
  5. 返回通过回收过滤器的每个项目的集合
  6. 返回最大元素E<?>结果
  7. 返回元素E<?>并使用基数2(二进制)调用方法toString并将其打印到屏幕
  8. 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> {
    

    这是我想在C级工作的。

     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'

    编辑:

    一口井,将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 个答案:

答案 0 :(得分:3)

您可以使用Scala吗?

它是一种可以在jdk下运行的函数式编程语言。

答案 1 :(得分:3)

您可以使用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]

答案 2 :(得分:2)

虽然我同意评论这不是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。

这不完全是我的头脑,所以我没有测试过一些潜在的通用问题。

答案 3 :(得分:1)

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

在java中处理不同的原始类型非常烦人,我不确定它是否可以轻松完成。你可能会失败一段时间,直到你做对了。也许你可以创建一个通用的Number类来抽象大小之间的差异(比如int,long,short等)和类型(整数,小数等),就像 cool 语言一样(Ruby,Smalltalk)例如)。根据结果​​的结果,您可以切换内部表示。例如,如果整数操作溢出,则切换为long。或者如果在操作中使用浮点数,则在内部切换到浮点数。但从外面来看,它只是一个数字。这会让你的其他课程更容易。

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

答案 4 :(得分:1)

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

相反,如果您有义务使用Java,请在http://code.google.com/p/lambdaj/处查看lambdaj

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

答案 5 :(得分:0)

Clojure是一种类似于lisp的函数式语言,可以在jvm上运行。它有 bignums,比率和bigdecimals与传统的无类型lisp语义。