Java как универсальный функциональный язык программирования высшего порядка - PullRequest
1 голос
/ 14 сентября 2009

Я решаю алгоритмические проблемы и хочу написать пользовательские функции и предикаты, которые можно применять к коллекциям. Недавно я начал использовать Google Collections, и это просто замечательно для этой задачи.

Я хочу использовать BigInteger и BigDecimal таким же образом, как и любые другие числа. Не задумываясь над тем, как это сделать, я решил создать дополнительный уровень абстракции (класс E).

Если неясно, что я пытаюсь сделать, вот пример:

I.range(1,999).multiplication(I.range(1,999)).palindromes().max().echo(2); 
  1. возврат коллекции из последовательности 1: 999 (x2)
  2. возвращает 2 коллекции для каждого элемента с помощью метода times () transform
  3. возвращает коллекцию каждого предмета, прошедшего фильтр палиндромов
  4. вернуть максимальный элемент E <?> Из 3. result
  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> {

Это то, что я хочу сделать, работая в классе 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' .

Edit:

Хорошо, изменил 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 ]

3 голосов
/ 16 сентября 2009

Вы можете использовать функциональную библиотеку 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));}}

РЕДАКТИРОВАТЬ вот как это выглядит с шумозащитными очками on:

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

main = putStrLn . maximum . filter palindrome $ tails xs >>= zipWith (*) xs
  where xs = [100..999]
3 голосов
/ 14 сентября 2009

Можете ли вы использовать Scala ?

Это функциональный язык программирования, который может работать под JDK.

2 голосов
/ 15 сентября 2009

Хотя я согласен с комментарием, что это не совсем так, как в 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.

Не совсем это у меня в голове, поэтому я не проверял некоторые потенциальные дженерики.

1 голос
/ 18 сентября 2009

Я считаю, что Scala может быть лучшим решением для ваших проблем.

Вместо этого, если вы обязаны использовать Java, посмотрите также на lambdaj на http://code.google.com/p/lambdaj/

что большая часть того, что вам нужно, уже реализована там.

1 голос
/ 15 сентября 2009

Вы можете использовать отражение. На основе имени класса найдите метод и вызовите его. Как, например, для «BigInteger» вы называете «BigIntegerValue» и т. Д.

Работа с различными примитивными типами в Java довольно раздражает, и я не уверен, что это можно сделать легко. Вы можете потерпеть неудачу несколько раз, пока не получите это правильно. Возможно, вы могли бы создать общий класс Number, который абстрагирует различия между размером (например, int, long, short и т. Д.) И типом (integer, decimal и т. Д.), Как у языков cool (Ruby, Smalltalk для пример). На основе результата результата вы переключаете внутреннее представление. Например, если целочисленные операции переполнены, вы переключаетесь на long. Или, если вы используете поплавок в операции, вы переключаетесь на поплавок внутри. Но снаружи это просто число. Это облегчит жизнь остальным ученикам.

Другой способ, вы могли бы написать генератор кода, чтобы написать все эти неприятные случаи для вас. Это не должно быть слишком сложно.

0 голосов
/ 15 сентября 2009

Clojure - это функциональный язык, похожий на lisp, который работает на jvm. Она имеет бигнумы, отношения и большие десятичные числа с традиционной семантикой без типа.

...