【问题标题】:Computing multiple results from one array iteration从一次数组迭代计算多个结果
【发布时间】:2011-07-31 02:01:46
【问题描述】:

我想在数组或列表的一次迭代中计算多个函数,但可能希望动态增长函数列表。例如,我可能想要计算最小值和最大值,然后,稍后某个时候还想要平均函数(或任何其他线性复杂度函数)。所以,我可能想要一个高级规范(使用一些名为 addFuncapplyAllFuncs 的函数,例如

funcs = emptyFunctionList;    # no functions in funcs "list" yet
funcs = addFunc (min, funcs);  # add minimum function
funcs1 = addFunc (max, funcs);  # add maximum function 

answers1 = applyAllFuncs (funcs1, myArray);     

funcs2 = addFunc (avg, funcs);  # add average function 

answers2 = applyAllFuncs (funcs2, myArray); 

我可以用函数式语言很容易地做到这一点,方法是从这些传递给foldl 的函数中创建一个“超级函数”,元编程可能是一种在 Haskell/C++ 中也可以更有效地做到这一点的机制,但是想知道是否有一种高效且不太复杂的方式,在标准 Java 中通常可以完成(或可以完成)(无需做很多工作来模拟/实现更高级别的功能)。

【问题讨论】:

    标签: java functional-programming performance metaprogramming iteration


    【解决方案1】:

    这是与您在帖子中提供的功能性伪代码等效的完整工作 Java 代码:

    import java.util.*;
    
    interface Function1<A, B> {
      public B apply(final A a);
    }
    
    class Main {
      public static <A, B> List<Function1<A, B>> addFunc(final Function1<A, B> f, final List<Function1<A, B>> fs) {
        final List<Function1<A, B>> gs = new ArrayList<Function1<A, B>>();
        gs.addAll(fs);
        gs.add(f);
        return gs;
      }
    
      public static <A, B> List<B> applyAllFuncs(final List<Function1<List<A>, B>> fs, final List<A> as) {
        final List<B> bs = new ArrayList<B>();
        for(final Function1<List<A>, B> f : fs) {
          bs.add(f.apply(as));
        }
        return bs;
      }
    
      public static Function1<List<Double>, Double> min = new Function1<List<Double>, Double>() {
        public Double apply(final List<Double> xs) {
          double mx = xs.get(0);
          for(final Double x : xs) {
            if(x < mx) {
              mx = x;
            }
          }
          return mx;
        }
      };
    
      public static Function1<List<Double>, Double> avg = new Function1<List<Double>, Double>() {
        public Double apply(final List<Double> xs) {
          double sum = 0;
          for(final Double x : xs) {
            sum += x;
          }
          return sum / xs.size();
        }
      };
    
      public static Function1<List<Double>, Double> max = new Function1<List<Double>, Double>() {
        public Double apply(final List<Double> xs) {
          double mx = xs.get(0);
          for(final Double x : xs) {
            if(x > mx) {
              mx = x;
            }
          }
          return mx;
        }
      };
    
      public static void main(final String[] args) {
        final List<Double> myArray = Arrays.asList(3.0, 8, 1, 2, 9);
        List<Function1<List<Double>, Double>> funcs = new ArrayList<Function1<List<Double>, Double>>();
        funcs = addFunc(min, funcs);
        final List<Function1<List<Double>, Double>> funcs1 = addFunc(max, funcs);
        final List<Double> answers = applyAllFuncs(funcs1, myArray);
        final List<Function1<List<Double>, Double>> funcs2 = addFunc(avg, funcs);
        final List<Double> answers2 = applyAllFuncs(funcs2, myArray);
        System.out.println(answers + "\n" + answers2);
      }
    }
    

    如果您使用现有的 Java 函数式编程库,例如 Functional Java 或 GridGain 提供的库,则可以避免大部分样板文件。

    这其中并没有多少被优化掉,因为 JVM 从来就不是为这类东西而设计的。 Scala 是 JVM 上的函数式语言,使用与上述相同的方法来实现 lambda 和更高阶函数,并提供与 Java 相当的性能。我仍然建议您应该分析代码并确定它是否满足您特定用例的性能要求。

    【讨论】:

    • 只是为了好玩,这就是等效代码在 Scala 中的外观:ideone.com/Fyokg
    【解决方案2】:

    创建您自己的折叠:
    有一个方法可以接收一个列表和一个要在列表上运行的函数列表。
    要在列表上运行的函数列表可以简单地是 ArrayList&lt;XFunction&gt; 其中 XFunction 是您定义的具有方法的类(例如 run(...) ),您可以为每个函数创建它的匿名实现。

    例如:

    list.add(new XFunction(){ 
       run(ArrayList<int> numbers){ 
          return numbers.size(); 
           }
       }
    

    这当然不是解决任何问题的函数,但你明白了

    【讨论】:

    • 非常感谢您的回答。这样做的一个意识形态问题是它正在以命令式语言实现功能范式。一个实际的问题是这将在多大程度上影响执行时间。我不确定有多少用于实现高阶函数(如您所示)的机器被标准 Java(Sun-but-now-Oracle)编译器优化掉了。
    • 我知道它们丑陋且不卫生,而且我根本不是一个大粉丝,尽管它可能是邪恶的,宏机制(如 C 迭代器宏)这将使我能够简单地内联附加结果计算,似乎是我能想到的解决实际问题的唯一方法。 ...或者也许我只是想得不够努力。
    【解决方案3】:

    您可以使用某种 Stategy 模式:

    public interface Function{
        public void Compute();
    }
    
    public Function1 implements Function{
       public void Compute(){
           System.out.println("This is Function1");
       }
    }
    
    public Function2 implements Function{
       public void Compute(){
           System.out.println("This is Function2");
       }
    }
    

    然后声明一个函数容器:

    ArrayList<Function> functions = new ArrayList<Function>();
    functions.add(new Function1());
    functions.add(new Function2());
    
    ArrayList<Object> someData = new ArrayList<Object>();
    Something.execute(someData,functions);
    

    【讨论】:

      猜你喜欢
      • 2020-08-09
      • 2022-07-24
      • 2019-07-23
      • 2021-04-28
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多