迭代函数

发布于 2024-08-10 22:26:26 字数 366 浏览 5 评论 0原文

在Java中可以做这样的事情吗?

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

我只调用了几个函数,但我需要组合它们,如下所示:

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

而且我想避免写出数百行函数调用。

我尝试研究函数指针和函子,但没有找到任何相关的东西。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

忆离笙 2024-08-17 22:26:26

您不能使用 f(g(o)) 语法,但可以使用(通过合适的接口)f.call(g.call(o))

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

示例用法(这与 Java 中的仿函数最接近,至少在闭包进入该语言之前):

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

如果您不想创建无数的类,基于反射的方法可能会更好(例如double -> java.lang.Math 中的 double 函数,但可以轻松适应其他场景):(

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

已省略异常消息显然,我将它们放入生产代码中。)

示例用法:

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

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));
    }
}
浊酒尽余欢 2024-08-17 22:26:26

Java 并没有真正精确地处理函子,但您可以通过接口来非常接近函子。我建议也许尝试这样的事情。

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

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

...

然后在您的代码中,您将创建一个包含 Function1、Function2 ... 对象的数组或列表,并运行看起来很像您的代码的内容。

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

或者,对于两层嵌套:

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

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));
            }
      }
}
信愁 2024-08-17 22:26:26

@Seth——这是你的泛型示例。由于泛型在运行时不存在,我不明白为什么您担心失去“灵活性”。如果您使用泛型,那么您只是在使用对象。

如果你希望 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));
    }
}

@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));
    }
}
〃安静 2024-08-17 22:26:26

也许你可以尝试一个流畅的界面,它可以让你将这些组合在一起。这可能是一个不错的设计,但我无法从你的例子中看出。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文