0

私は Java でいくつかの数値計算を行い、操作を本当にモジュール化するために、関数を他の関数のパラメーターとして渡したいと考えています。私は検索していましたが、通常は関数をワープするクラスを使用してJavaで行われます。私は実際にこれらのクラスをインスタンス化する必要はなく (内部にデータはありません)、可能な限り高速にしたいと考えています (最終的な静的メソッドは JIT コンパイラによってインライン化されるとどこかに書かれています)。だから私はこのようなものを作りました

public static class Function2 {
  public static float eval(float a, float b){ return Float.NaN; }  
}

public static class FAdd extends Function2 {
  public static float eval(float a, float b){ return a+b; }  
}

public static class Fmult extends Function2 {
  public static float eval(float a, float b){ return a*b; }  
}

void arrayOp( float [] a, float [] b, float [] out, Function2 func ){
  for (int i=0; i<a.length; i++){     out[i] = func.eval( a[i], b[i] );   }
}

float [] a,b, out;

void setup(){
  println( FAdd.eval(10,20) );
  arrayOp( a,b, out, FAdd );
}

ただし、println( FAdd.eval(10,20) ) が正常に動作しているにもかかわらず、arrayOp に渡そうとすると、「FAdd のようなものが見つかりません」というエラーが出力されます。そのため、何らかの理由で静的クラスをパラメーターとして渡すことは不可能のようです。

そのようなタスクを解決するために何をお勧めしますか? 実際には、FAdd をマクロのようなものにしたいのですが、arrayOp を polymorf にする必要があります (渡すマクロに応じて動作します)。しかし、計算速度を向上させるために、(実行時ではなく) コンパイル時に解決されるのが理想的です。コンパイルされた結果は、私が書いた場合と同じになるはずです

void arrayAdd( float [] a, float [] b, float [] out ){
  for (int i=0; i<a.length; i++){     out[i] = a[i]  + b[i];    }
}
void arrayMult( float [] a, float [] b, float [] out ){
  for (int i=0; i<a.length; i++){     out[i] = a[i] * b[i];   }
} 
4

6 に答える 6

4

列挙型の使用を検討しましたか?

private void test() {
  test(3.0f, 4.0f, F.Add);
  test(3.0f, 4.0f, F.Sub);
  test(3.0f, 4.0f, F.Mul);
  test(3.0f, 4.0f, F.Div);
  float[] a = {1f, 2f, 3f, 4f, 5f};
  float[] b = {4f, 9f, 16f, 25f, 36f};
  test(a, b, F.Add);
  test(a, b, F.Sub);
  test(a, b, F.Mul);
  test(a, b, F.Div);
}

private void test(float[] a, float[] b, F f) {
  System.out.println(Arrays.toString(a) + " " + f + " " + Arrays.toString(b) + " = " + Arrays.toString(f.f(a, b, f)));
}

private void test(float a, float b, F f) {
  System.out.println(a + " " + f + " " + b + " = " + f.f(a, b));
}

public enum F {
  Add {
    @Override
    public float f(float x, float y) {
      return x + y;
    }

    @Override
    public String toString() {
      return "+";
    }
  },
  Sub {
    @Override
    public float f(float x, float y) {
      return x - y;
    }

    @Override
    public String toString() {
      return "-";
    }
  },
  Mul {
    @Override
    public float f(float x, float y) {
      return x * y;
    }

    @Override
    public String toString() {
      return "*";
    }
  },
  Div {
    @Override
    public float f(float x, float y) {
      return x / y;
    }

    @Override
    public String toString() {
      return "/";
    }
  };

  // Evaluate to a new array.
  static float[] f(float[] x, float[] y, F f) {
    float[] c = new float[x.length];
    for (int i = 0; i < x.length; i++) {
      c[i] = f.f(x[i], y[i]);
    }
    return c;
  }

  // All must have an f(x,y) method.
  public abstract float f(float x, float y);

  // Also offer a toString - defaults to the enum name.  
  @Override
  public String toString() {
    return this.name();
  }
}

版画:

3.0 + 4.0 = 7.0
3.0 - 4.0 = -1.0
3.0 * 4.0 = 12.0
3.0 / 4.0 = 0.75
[1.0, 2.0, 3.0, 4.0, 5.0] + [4.0, 9.0, 16.0, 25.0, 36.0] = [5.0, 11.0, 19.0, 29.0, 41.0]
[1.0, 2.0, 3.0, 4.0, 5.0] - [4.0, 9.0, 16.0, 25.0, 36.0] = [-3.0, -7.0, -13.0, -21.0, -31.0]
[1.0, 2.0, 3.0, 4.0, 5.0] * [4.0, 9.0, 16.0, 25.0, 36.0] = [4.0, 18.0, 48.0, 100.0, 180.0]
[1.0, 2.0, 3.0, 4.0, 5.0] / [4.0, 9.0, 16.0, 25.0, 36.0] = [0.25, 0.22222222, 0.1875, 0.16, 0.1388889]
于 2013-01-15T13:40:53.757 に答える
1

あなたが達成したいのは、実際には匿名関数またはラムダ式の機能です。これは、JSR 335 (Java プログラミング言語のラムダ式) にあり、Java 8 で利用できるようになります。現在、それに近いのは匿名内部クラスだけです。スタックオーバーフローのこの質問( Java で関数ポインターの最も近い代替手段は何ですか? ) が役立つ場合があります。

于 2013-01-15T13:43:03.427 に答える
1

あなたは、最終的な静的メソッドである場合にのみ最速のコードになるという大規模な仮定を立てています。あなたは間違っている可能性が高く、適切に設計してパフォーマンスをテストすることに集中する必要があります。

上記のように、1つの方法は敵を使用することです。私がすべきことは、 eval 関数とのインターフェースを持つことだと思います。その後、インターフェースの実装を渡すことができます。

Java VM は、そのコードを適切に最適化します。

于 2013-01-15T13:54:03.170 に答える
1

静的メソッドはオーバーライドできませんが、匿名クラスを使用してオーバーライドできます。

public static class Function2 {
    public float eval(float a, float b){ return Float.NaN; }  
}

arrayOp(a, b, out, new Function2() {
    public float eval(float a, float b){
        return FAdd.eval(a, b);
    }});

Function2 の eval() のメソッド宣言は静的ではないことに注意してください。

于 2013-01-15T13:55:17.913 に答える
0

いくつかのテストを行いましたが、実際には、最新のマシンで最適化を試みる必要はないようです。

マシン 1 - (私の古いホーム コンピューター) 32 ビット WinXP、Intel Pentium 3、(Java バージョンについてはわかりません) float.mult と float.add の両方の操作で、静的バージョンは 2 倍以上高速です

static  100000000 [ops]  406.0 [s]  4.06 [ns/op] 
dynamic 100000000 [ops]  1188.0 [s]  11.88 [ns/op] 

しかし、float Sqrt の場合、差はすでに非常に小さい

static  100000000 [ops]  922.0 [s]  9.22 [ns/op] 
dynamic 100000000 [ops]  1172.0 [s]  11.719999 [ns/op] 

マシン 2 - (作業中の私のコンピューター) - 64 ビット ubuntu 12.04LTS、Intel Core5、Java バージョン "1.6.0_12-ea、Java(TM) SE ランタイム環境 (ビルド 1.6.0_12-ea-b02)、Java HotSpot(TM) 64 ビット サーバー VM (ビルド 11.2-b01、混合モード) 結果ははるかに優れています (float.add の場合):

static  1000000000 [ops]  1747.0 [s]  1.7470001 [ns/op] 
dynamic 1000000000 [ops]  1750.0 [s]  1.75 [ns/op] 

したがって、プロセッサまたは JIT はすでに十分に賢いので、とにかくこの関数の受け渡しを最適化する必要はないと思います。

注:-関数を渡さない静的平均ソリューション(手動で操作をループにインライン化するだけです)、-関数を動的オブジェクトインスタンス(静的クラスではなく)として使用する場合の動的平均ソリューション。JIT はクラス内に動的データがないことを理解しているようで、とにかくコンパイル時に解決します。

私の動的ソリューションは単純です:

public class Function2 {
  public float eval(float a, float b){ return Float.NaN; }  
}

public class FAdd extends Function2 {
  public float eval(float a, float b){ return a+b; }
}

public class FMult extends Function2 {
  public float eval(float a, float b){ return a*b; }  
}

public void arrayOp( float [] a, float [] b, float [] out, Function2 func ){
  for (int i=0; i<a.length; i++){     out[i] = func.eval( a[i], b[i] );   }
}

final int m = 100;
final int n = 10000000;
float t1,t2;
float [] a,b, out;
a = new float[n];   b = new float[n];   out = new float[n];
t1 = millis();
Function2 func = new FMult(); 
for (int i=0;i<m;i++) arrayOp( a,b, out, func );
t2 = millis();
println( " dynamic " +(n*m)+" [ops]  "+(t2-t1)+" [s]  "+ 1000000*((t2-t1)/(n*m))+" [ns/op] " );
于 2013-01-16T11:05:04.023 に答える
0

実際には、実装でインスタンスとクラスを混同しています。次のように宣言されたメソッドがある場合:

void arrayOp( float [] a, float [] b, float [] out, Function2 func ){
   for (int i=0; i<a.length; i++){     out[i] = func.eval( a[i], b[i] );   }
}

Function2あなたは基本的に、実際には class パラメータではなく、class のインスタンスを期待していると言っています。また、このステートメントは構文的に正しくありません。

arrayOp( a,b, out, FAdd );

クラス自体をメソッドに送信したい場合、arrayOp の宣言は次のようになります。

void arrayOp( float [] a, float [] b, float [] out, Class func ){

このメソッドを呼び出すときは、次のようにパラメーターを渡します。

arrayOp( a,b, out, FAdd.class );

ただし、継承によって静的メソッドをオーバーライドすることはできません。目標を達成するには、まったく異なる実装が必要です。そうは言っても、@OldCurmudgeon はあなたの問題に対して本当に素晴らしい解決策を提示してくれました。それを利用することを検討してください。

于 2013-01-15T13:53:20.087 に答える