一種高效的 vector 四則運算處理方法

2021-07-15 02:26:06 字數 3490 閱讀 4020

這裡假設 vector 的運算定義為對運算元 vector 中相同位置的元素進行運算,最後得到乙個新的 vector。具體來說就是,假如vectord1, d2;則,v1 + v2等於。實現這樣的運算看起來並不是很難,乙個非常直觀的做法如下所示:

vector

> operator

+(const

vector

>& v1, const

vector

>& v2)

return

r;}// 同理,需要過載其它運算子

我們針對 vector 過載了每種運算子,這樣一來,vector 的運算就與一般簡單型別無異,實現也很直白明了,但顯然這個直白的做法有乙個嚴重的問題:效率不高。效率不高的原因在於整個運算過程中,每一步的運算都產生了中間結果,而中間結果是個 vector,因此每次都要分配記憶體,如果參與運算的 vector 比較大,然後運算又比較長的話,效率會比較低,有沒有更好的做法呢?

既然每次運算產生中間結果會導致效率問題,那能不能優化掉中間結果?回過頭來看,這種 vector 的加減乘除與普通四則運算並無太大差異,在編譯原理中,對這類表示式進行求值通常可以通過先把表示式轉為一棵樹,然後通過遍歷這棵樹來得到最後的結果,結果的計算是一次性完成的,並不需要儲存中間狀態,比如對於表示式:v1 + v2 * v3,我們通常可以先將其轉化為如下樣子的樹:

因此求值就變成一次簡單的中序遍歷,那麼我們的 vector 運算是否也可以這樣做呢?

要把中間結果去掉,關鍵是要推遲對表示式的求值,但 c++ 不支援 lazy evaluation,因此需要想辦法把表示式的這些中間步驟以及狀態,用乙個輕量的物件儲存起來,具體來說,就是需要能夠將表示式的中間步驟的運算元以及操作型別封裝起來,以便在需要時能動態的執行這些運算得到結果,為此需要定義類似如下這樣乙個類:

enum

optype ;

class

vectmp

intoperator

(const

inti) const}};

有了這個類,我們就可以把乙個簡單的運算表示式的結果封裝到乙個物件裡面去了,當然,我們得先將加法操作符(以及其它操作符)過載一下:

vectmp operator

+(const

vector

>& op1, const

vector

>& op2)

這樣一來,對於v1 + v2,我們就得到了乙個非常輕量的 vectmp 物件,而該物件可以很輕鬆地轉化為v1 + v2的結果(遍歷一遍 vectmp 中儲存的運算元)。但上面的做法還不能處理v1 + v2 * v3這樣的套嵌的複雜表示式:v2 * v3得到乙個 vectmp,那v1 + vectmp怎麼搞呢?

同理,我們還是得把v1 + vectmp放到乙個輕量的物件裡,因此最好我們的 vectmp 中儲存的運算元也能是 vectmp 型別的,有點遞迴的味道。。。用模板就可以了,於是得到如下**:

#include

#include

using

namespace

std;

enum

optype ;

template

t1, class

t2>

class

vecsum

intoperator

(const

inti) const

}};template

t1, class

t2>

vecsumoperator

+(const

t1& t1, const

t2& t2)

template

t1, class

t2>

vecsumoperator

*(const

t1& t1, const

t2& t2)

intmain

(), v2, v3;

auto

r = v1 + v2 * v3;

for(auto

i = 0; i < r.size(); ++i)

}

上面的**漂亮地解決了前面提到的效率問題,擴充套件性也很好而且對 vector 來說還是非侵入性的,雖然實現上乍看起來可能不是很直觀,除此也還有些小問題可以更完善些:

操作符過載那裡很可能會影響別的型別,因此最好限制一下,只針對 vector 和 vectmp 進行過載,這裡可以用 sfinae 來處理。

vectmp 的 operator 函式中的 switch 可以優化掉,vectmp 模板只需增加乙個引數,然後對各種運算型別進行偏特化就可以了。

vectmp 對儲存的運算元是有要求的,只能是 vector 或者是 vectmp<>,這裡也應該用 sfinae 強化一下限制,使得用錯時出錯資訊好看些。

現在我們來重頭再看看這一小段奇怪的**,顯然關鍵在於 vectmp 這個類,我們可以發現,它的介面其實很簡單直白,但它的型別卻可以是那麼地複雜,比如說對於 v1 + v2 * v3 這個表示式,它的結果的型別是這樣的:vectmp, vectmp, vector>>,如果表示式再複雜些,它的型別也就更複雜了,如果你看仔細點,是不是還發現這東西和**很像?像一棵樹,一棵型別的樹。

這棵樹看起來是不是還很眼熟,每個葉子結點都是 vector,而每個內部結點則是由 vectmp 例項化的:這是一棵型別的樹,在編譯時就確定了。這種通過表示式在編譯時得到的複雜型別有乙個學名叫: expression template。在 c++ 中每乙個表示式必產生乙個結果,而結果必然有型別,型別是編譯時的東西,結果卻是執行時的。像這種運算表示式,它的最終型別是由其中每一步運算所產生的結果所對應的型別組合起來所決定的,型別確定的過程其實和表示式的識別是一致的。

vectmp 物件在邏輯上其實也是一棵樹,它的成員變數 op1_, op2_ 則分別是左右兒子結點,樹的內部結點代表乙個運算,葉子結點則為運算元,一遍中序遍歷下來,得到的就是整個表示式的值。

expression template 是個好東西(就正如 expression sfinae 一樣),它能幫助你在編譯時建立非常複雜好玩的型別系統(從而實現很多高階玩意,主要是函式式)。但顯然如果什麼東西都需要自己從頭開始寫,這個技術用起來還是很麻煩痛苦的,好在模板元程式設計實在是個太好玩的東西,已經有很多人做了很多先驅性的工作,看看 boost proto 吧,在 c++ 的世界裡再開啟一扇通往奇怪世界的大門。

python四則運算程式 四則運算(Python)

四則運算程式 一 資訊 二.題目要求 寫乙個能自動生成小學四則運算題目的程式,然後在此基礎上擴充套件 除了整數以外,還要支援真分數的四則運算,例如 1 6 1 8 7 24 程式要求能處理使用者的輸入,判斷對錯,累積分數 程式支援可以由使用者自行選擇加 減 乘 除運算 三 import random...

java四則運算

public class arithmetic implements serializable 除法 param number1 除數 param number2 被除數 param decimal 保留幾位小數點 return public static double divide string ...

C 四則運算

一 問題及 檔名稱 兩個浮點數的四則運算 02.作 者 李欽 03.完成日期 2016 年 3 月 12 日 04.版 本 號 v1.0 05.對任務及求解方法的描述部分 06.輸入描述 07.問題描述 略 08.程式輸出 略 09.問題分析 略 10.演算法設計 略 11.includevoid ...