简体   繁体   English

遍历函数

[英]Iterate over functions

Is something like this possible to do in Java? 在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)) . 您不能使用f(g(o))语法,但是可以使用f.call(g.call(o)) (具有适当的接口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): 用法示例(这与Java中的函子很接近,至少直到闭包成为该语言为止):

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): 如果你不希望创建一个数不胜数类,基于反射的方法可能更好地工作(例如对于double - > double功能java.lang.Math ,但很容易适应其他场景):

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

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. 这可能是一个不错的设计,但我不能从您的示例中看出。

Java doesn't really do functors exactly, but you can get pretty close with an interface. Java并没有真正真正地完成函子,但是您可以通过一个界面非常接近它。 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. 然后,在您的代码中创建一个包含Function1,Function2 ...对象的数组或列表,并运行类似于您的代码的内容。

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. @Seth-这是您的泛型示例。 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. 如果您希望F的行为根据G的返回类型而有所不同,那么您只需声明F做类似F的事情,轻松自在。

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

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

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