简体   繁体   English

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

[英]Java as generic higher-order functional programming language

I'm solving algorithmical problems, and i want to write custom functions and predicates that can be applied to collections. 我正在解决算法问题,我想编写可应用于集合的自定义函数和谓词。 Recently, i started using Google Collections and it is just wonderful for this task. 最近,我开始使用Google收藏夹,这对于完成这项任务来说真是太好了。

I want to use BigInteger and BigDecimal same way, as i would use any other numbers. 我想以相同的方式使用BigInteger和BigDecimal,就像我将使用任何其他数字一样。 Without pondering how it should be done, i decided to make an extra layer of abstraction(Class E). 在不考虑应该怎么做的情况下,我决定增加一个额外的抽象层(E类)。

If it is unclear what I am trying to do, here is an example: 如果不清楚我要做什么,可以举一个例子:

I.range(1,999).multiplication(I.range(1,999)).palindromes().max().echo(2); 
  1. return collection of sequence 1:999 (x2) 返回序列1:999的集合(x2)
  2. return 2 collections every item with method times() transform 使用times()转换方法返回每个项目2个集合
  3. return collection of every item what passes palindromes filter 返回通过回文过滤器的每个项目的集合
  4. return maximum element E<?> of 3. result 返回3的最大元素E <?>
  5. return element E<?> and invoke method toString with radix 2 (binary) and print it to screen 返回元素E <?>并调用基数为2(二进制)的toString方法并将其打印到屏幕上

Class E is defined as: E类定义为:

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

Class C is defined as: C类定义为:

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

This is what i want to make working in class C. 这就是我要在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);
   }
  });
 }

Before i used following code, in class E 在我使用以下代码之前,在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();
 }

What should i change, so that writing custom functions and predicates would involve minimal amount of 'suckiness' . 我应该改变什么,以便编写自定义函数和谓词将涉及最少的“麻烦”

Edit: 编辑:

A well, changed C to: 好吧,将C更改为:

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

And just suppressed generic wildcard casting warnings like 只是取消了通用通配符转换警告,例如

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

So if the types match, this works. 因此,如果类型匹配,则可行。

Can you use Scala ? 可以使用Scala吗?

It is a functional programming language that can run under the jdk. 它是一种可以在jdk下运行的功能性编程语言。

You could use the Functional Java library. 您可以使用Functional Java库。

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

EDIT here's what it looks like with noise-filtering glasses on: 编辑 ,下面是使用噪音过滤眼镜的外观:

palindrome i = s == reverse s
  where s = show i

main = putStrLn . maximum . filter palindrome $ tails xs >>= zipWith (*) xs
  where xs = [100..999]

Although I agree with the comment that this isn't quite up Java's alley, one way to approach this is might be to initialize a Map like this: 尽管我同意这不是Java的小巷,但解决此问题的一种方法可能是初始化Map,如下所示:

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

etc. for each case. 等等。 Then in your code, (with an appropriate null check): 然后在您的代码中(进行适当的null检查):

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

Note, that the raw type here is on purpose, I would have to think if this could be done and keep everything generic. 注意,这里的原始类型是有目的的,我必须考虑是否可以做到这一点,并使所有内容保持通用。 Not easily, anyway. 无论如何,都不容易。

But given the class of val, you could retrieve an appropriate E. 但是给定val的类别,您可以检索适当的E。

Not this is entirely out of my head, so I haven't tested some of the potential generic gotchas. 这不是完全超出我的脑海,所以我还没有测试一些潜在的通用陷阱。

You could use reflection. 您可以使用反射。 Based on the name of the class, find the method and invoke it. 根据类的名称,找到方法并调用它。 Like for example for "BigInteger" you call "BigIntegerValue" and so on. 例如,对于“ BigInteger”,您称为“ BigIntegerValue”,依此类推。

Dealing with the different primitive types in java is quite annoying, and I'm not sure it can be done easily. 在Java中处理不同的原始类型非常烦人,我不确定是否可以轻松实现。 You might fail a few time until you get it right. 您可能会失败几次,直到正确为止。 Perhaps you could create a generic Number class that abstracts away the differences between size (like int, long, short etc.) and type (integer, decimal etc.) like the cool languages have (Ruby, Smalltalk for example). 也许您可以创建一个通用的Number类,以抽象出大小(如int,long,short等)和类型(整数,十进制等)之间的差异,就像语言(例如Ruby,Smalltalk)一样。 Based on the outcome of the result you switch the internal representation. 根据结果​​的结果,您可以切换内部表示。 For example, if integer operations overflow, you switch to long. 例如,如果整数运算溢出,则切换到long。 Or if you use a float in the operation you switch to float internally. 或者,如果在操作中使用浮点数,则切换为内部浮点数。 But from the outside it's just a number. 但是从外面看,这只是一个数字。 That would make things easier for the rest of your classes. 这将使其余课程的学习变得容易。

Another way, you could write a code generator to write all those nagging cases for you. 换句话说,您可以编写一个代码生成器来为您编写所有这些烦人的案例。 It shouldn't be too complicated. 它不应该太复杂。

I believe Scala could be the best solution for your problems. 我相信Scala可能是解决您问题的最佳解决方案。

Instead, if you are obliged to use Java, take also a look to lambdaj at http://code.google.com/p/lambdaj/ 相反,如果您必须使用Java,请访问http://code.google.com/p/lambdaj/并访问lambdaj。

You will discover that the biggest part of the things you need as been already implemented there. 您会发现,所需的最大部分已经在此处实现。

Clojure is a lisp-like functional language that runs on jvm. Clojure是在jvm上运行的类似lisp的功能语言。 It has bignums, ratios and bigdecimals with traditional typeless lisp semantics. 它具有传统的无类型lisp语义的bignums,ratios和bigdecimals。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM