views:

440

answers:

6

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.

I want to use BigInteger and BigDecimal same way, as i would use any other numbers. Without pondering how it should be done, i decided to make an extra layer of abstraction(Class 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)
  2. return 2 collections every item with method times() transform
  3. return collection of every item what passes palindromes filter
  4. return maximum element E<?> of 3. result
  5. return element E<?> and invoke method toString with radix 2 (binary) and print it to screen

Class E is defined as:

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

Class C is defined as:

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.

 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

 /** 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:

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.

+3  A: 

Can you use Scala?

It is a functional programming language that can run under the jdk.

James Black
Short answer is: I can, but I do not want to.
Margus
My suggestion is that you change it so that you do want to! This problem looks like the kind of thing that might play to Scala's strengths.
Joe
I prefer Python, Mathlab, J, Haskell, Prolog, F#, oCaml et cetera to Scala (in that order). Right now, i want to use Google Collections in Java. Besides i am aware of scala, but not familiar with it.
Margus
So this isn't a functional programming question, but, how can I force Google Collections to work in this situation? You may want to change the tags then.
James Black
Well, if you prefer Python, you could use Jython which runs on the JVM.
Andrei Vajna II
Although I disagree with James fundamentalist approach on the word "Functional", I might point out that although I truly love Java in every sense of the word, it is not optimized for Functional programming where Scala and Haskell would be. If @Margus had looked at Scala and rejected it because of it's complexity I'd totally be there with him--but it doesn't seem right to not "prefer" something you don't know... I thought preference implied some level of sampling/understanding. I'd at least have a look at it (It should support the Google libraries directly anyway)
Bill K
I gave a link to an overview of Scala as a starting point, to help introduce people that know Java to it.
James Black
I guess Scala could not be worse then Haskell, and i will take a try with it also.
Margus
+2  A: 

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:

  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):

  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.

Not this is entirely out of my head, so I haven't tested some of the potential generic gotchas.

Yishai
+1  A: 

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.

Dealing with the different primitive types in java is quite annoying, and I'm not sure it can be done easily. 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). Based on the outcome of the result you switch the internal representation. For example, if integer operations overflow, you switch to 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.

Andrei Vajna II
A: 

Clojure is a lisp-like functional language that runs on jvm. It has bignums, ratios and bigdecimals with traditional typeless lisp semantics.

ddyer
+3  A: 

You could use the 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));}}

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]
Apocalisp
Well i got my problem solved 15m after posting my question, but you surely have closest answer that i was looking for.
Margus
Man that looks ugly! :)
Andrei Vajna II
Train your eyes to ignore redundant parentheses, semicolons, braces, and type annotation. Java looks nicer that way.
Apocalisp
+1 for "noise-filtering glasses" ;-)
missingfaktor
IntelliJ actually can be configured to fold away a lot of the cruft and make it look like Java 6 has closures.
retronym
+1  A: 

I believe Scala could be the best solution for your problems.

Instead, if you are obliged to use Java, take also a look to lambdaj at http://code.google.com/p/lambdaj/

You will discover that the biggest part of the things you need as been already implemented there.

Mario Fusco