2013-01-15 128 views
0

我想在java中進行一些數值計算,並且使操作真正模塊化,我希望將函數作爲其他函數的參數傳遞。我正在尋找,通常它是用java來完成的,這個類使用了這個函數。我真的不需要實例化這些類(裏面沒有數據),我想盡可能快地完成它(最終靜態方法由JIT編譯器內聯)。所以我做了這樣的事情將函數作爲Java中的快速數字的靜態類使用

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

但是它打印錯誤:「找不到類似的東西FADD」當我試圖把它傳遞給arrayOp,即使的println(FAdd.eval(10,20))正常工作。所以看起來由於某種原因,將靜態類作爲參數傳遞是不可能的。

你推薦解決這樣的任務嗎?我真的希望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]; } 
} 
+0

函數是在Java中沒有第一類對象,所以這是不可能的與Java <= 7。不確定Java 8.相反,Python或Ruby允許你這樣做。 – Makoto

+0

結果將是一樣的,但它不會像任何需要一樣快。如果性能至關重要,您應該提供最終的個人方法。 –

+1

@Makoto方法將成爲Java 8中的頭等對象,但尚未發佈。 ;) –

回答

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] 
+0

這是一個非常好的技術,爲解決方案+1。 –

+0

然而,認識到這與爲每個操作定義單獨的類和實例沒有什麼不同。這只是少打字。 – parsifal

+0

@parsifal - 不僅僅是簡單的打字,而且還遵守各種編碼原則,如[最小驚訝](http://en.wikipedia.org/wiki/Principle_of_least_astonishment)等。此外 - 每個類只有一次實例化。 – OldCurmudgeon

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類的實例,而不是一個真正的類參數。同樣這句話語法不正確:

arrayOp(a,b, out, FAdd); 

因此,可以說你要到類自身發送到的方法,那麼你的arrayOp的聲明看起來是這樣的:

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

當你調用這個您將以這種方式傳遞參數的方法:

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

但是靜態方法不能通過繼承重寫。你需要一個完全不同的實現來實現你的目標。這就是說@OldCurmudgeon爲你的問題提出了一個非常好的解決方案。考慮使用它。

1

您正在做出一些大規模的假設,即最快的代碼只有在最終的靜態方法時纔是如此。你很可能是錯的,應該專注於正確構建它並測試性能。

一種方法是使用敵人,如上所述。我會說喲應該做的是與eval函數有一個接口。然後你可以傳入一個接口的實現。

Java VM將實現適當的優化代碼。

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

注意,在EVAL的方法聲明()的函數2也不是一成不變的。

+0

我不喜歡這裏的「新」,它會分配新的對象。 (我不確定它是否在JIT中進行了某種優化,在編譯期內可以內聯沒有數據的新對象?) –

0

我做了一些測試,似乎真的沒有必要嘗試在現代機器上優化它。

機1 - (我的舊的家用電腦)32位的WinXP,英特爾奔騰3,(我不知道關於Java版本) 對於這兩種操作float.mult和float.add靜態版本是超過2X快

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

但浮法開方差異已經非常小

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,英特爾Core5,Java版本「1.6.0_12-EA, Java TM SE運行時環境(build 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已經聰明到足夠多的該療法是沒必要無論如何優化這個函數傳遞。

注: - 沒有通過功能靜態平均劑(I手動只是直列 操作進入死循環), - 動態意味着解決方案時,我使用的傳遞函數動態對象實例(不是靜態類)。看起來,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] ");