开发者

Iterate over functions

Is something like this possible to do in Java?开发者_如何学运维

for (Object o : objects) {
  for (Function f : functions) {
    f(o);
  }
}

I'm only calling a handful of functions, but I need to compose them, like so:

for (Object o : objects) {
  for (Function f : functions) {
    for (Function g : functions) {
      f(g(o));
    }
  }
}

And I'd like to avoid writing out hundreds of lines of function calls.

I've tried researching function pointers and functors, but haven't found anything pertinent.


You can't use the f(g(o)) syntax, but you can use (with a suitable interface) f.call(g.call(o)).

public interface UnaryFunction<Arg, Ret> {
    Ret call(Arg arg);
}

Example usage (this is as close as you can get to functors in Java, at least until closures make it into the language):

public class Exp implements UnaryFunction<Double, Double> {
    public Double call(Double arg) {
        return Math.exp(arg);
    }
}

If you don't want to create a zillion classes, a reflection-based approach may work better (example for double -> double functions in java.lang.Math, but easily adaptable to other scenarios):

public class MathUnary implements UnaryFunction<Double, Double> {
    private final Method method;

    public MathUnary(String funName) {
        try {
            method = Math.class.getMethod(funName, double.class);
        } catch (NoSuchMethodException exc) {
            throw new IllegalArgumentException(exc);
        }
        if (method.getReturnType() != double.class)
            throw new IllegalArgumentException();
    }

    public Double call(Double arg) {
        try {
            return (Double) method.invoke(null, arg);
        } catch (IllegalAccessException exc) {
            throw new AssertionError(exc);
        } catch (InvocationTargetException exc) {
            throw new AssertionError(exc);
        }
    }
}

(Exception messages have been left out for brevity. Obviously, I'd put them in for production code.)

Sample usage:

MathUnary[] ops = {
    new MathUnary("sin"), new MathUnary("cos"), new MathUnary("tan")
};

for (UnaryFunction<Double, Double> op1 : ops) {
    for (UnaryFunction<Double, Double> op2 : ops) {
        op1.call(op2.call(arg));
    }
}


Java doesn't really do functors exactly, but you can get pretty close with an interface. I'd reccommend maybe trying something like this.

public interface Function {
    Object doWork(Object o);
}

public class Function1 implements Function {
    public Object doWork(Object o) {
        ...
    }
}

...

And then in your code you'd create an array or list containing Function1, Function2 ... objects and run something that looks a lot like your code.

for (Object o : objects) {
      for (Function f : functionList) {
          f.doWork(o);
      }
}

Or, for two levels of nesting:

for (Object o : objects) {
      for (Function f : functionList1) {
            for (Function g : functionList2) {
                f.doWork(g.doWork(o));
            }
      }
}


@Seth -- Here is your example with generics. Since generics don't exist at runtime, I do not understand why you fear a loss of "flexibility". If you use generics, then you are just using Objects.

If you want F's behavior to vary based on the return type of G, then you would just declare your F to do something like F, easy peasy.

//=== Function.java

public interface Function<ReturnType, Type> {
    ReturnType doWork(Type arg);
}

//=== SomethingWeird.java

import java.util.*;

// yo dawg, i heard you liked functions.  so i put a function in yo'
// function, so you can derive while you derive.
public class SomethingWeird {
    public static <FReturnType, FType, GType> List<FReturnType> collateOrSomething(
        Iterable<GType> objects,
        Iterable<Function<FReturnType, FType>> fList,
        Iterable<Function<FType, GType>> gList
    ) {
        List<FReturnType> results = new ArrayList<FReturnType>();
        for (GType garg : objects) {
            for (Function<FReturnType, FType> f : fList) {
                for (Function<FType, GType> g : gList) {
                    results.add(f.doWork(g.doWork(garg)));
                }
            }
        }
        return results;
    }
}

//=== SomethingWeirdTest.java

import java.util.*;

import org.junit.*;
import static org.junit.Assert.*;

public class SomethingWeirdTest {
    // this is kinda silly, and...
    public static class F1 implements Function<Integer, Double> {
        @Override
        public Integer doWork(Double arg) {
            return arg.intValue();
        }

    }

    // ...this has all kinds of autoboxing madness, but...
    public static class F2 implements Function<Integer, Double> {
        @Override
        public Integer doWork(Double arg) {
            double ceil = Math.ceil(arg);
            return (int) ceil;
        }       
    }


    // ...why you'd want to do something like this is quite beyond me...
    public static class G1 implements Function<Double, String> {
        @Override
        public Double doWork(String arg) {
            return Math.PI * arg.length();
        }
    }

    // ...ditto this...
    public static class G2 implements Function<Double, String> {
        @Override
        public Double doWork(String arg) {
            return Math.E * arg.length();
        }

    }

    // oh, yeah, it was so we could test this weird thing
    @Test
    public void testCollateOrSomething() {
        List<String> data = Arrays.asList("x", "xx", "xxx");
        List<Function<Integer, Double>> fList = Arrays.asList(new F1(), new F2());
        List<Function<Double, String>> gList = Arrays.asList(new G1(), new G2());
        List<Integer> results = SomethingWeird.collateOrSomething(data, fList, gList);

        assertEquals(12, results.size());

        // x
        assertEquals(3, (int) results.get(0));
        assertEquals(2, (int) results.get(1));
        assertEquals(4, (int) results.get(2));
        assertEquals(3, (int) results.get(3));

        // xx
        assertEquals(6, (int) results.get(4));
        assertEquals(5, (int) results.get(5));
        assertEquals(7, (int) results.get(6));
        assertEquals(6, (int) results.get(7));

        // xxx
        assertEquals(9, (int) results.get(8));
        assertEquals(8, (int) results.get(9));
        assertEquals(10, (int) results.get(10));
        assertEquals(9, (int) results.get(11));
    }
}


Maybe you can try a fluent interface that would let you gang these together. It might be a nice design, but I can't tell from your example.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜