值相同, 但我們知道這兩個 Foo 操作是不同的
0, 1, 2, 3, 4
在很多語言, 退出迴圈之後還可以拿到區域變數 i, 若拿的到並列印出來, 就會是
5
為了確定大家真的了解, 請試著回答以下迴圈, 每次的 ( n, i ) 值為何 ?
來看答案, 是否跟你想的一樣呢 ?
再用 JavaScript 版本來確認, 是一致的結果,
複習一下好了, The Java™ Tutorials 針對 Loop 的介紹
注意 n = i++ 的版本, n 值在第三段 increment 拿到的是尚未 i + 1 的版本 !
假設有個 2 維矩陣類別 Matrix2d, 想要進行矩陣 A++ 或 ++A (矩陣內每個元素值 +1)
A =
x1 y1
x2 y2
A++ =
x1+1 y1+1
x1+1 y1+1
++A =
x1+1 y1+1
x1+1 y1+1
Foo( x++ ); // post-increment
Foo( ++x ); // pre-increment
大家比較常用的 i++, x++... 我們稱為 post-increment, 是在這一行程式完成之後, 再進行 +1 的動作, 反之稱之為 pre-increment。所以其實上面例子可以理解為如下 (功能上, 非編譯器行為), 更囉嗦的版本。// Foo ( x++ )
Foo( x );
x = x + 1;
// Foo ( ++x )
x = x + 1;
Foo( x );
wiki 的例子更清楚, 請特別注意 y 的值。int x, y;
// Increment operators
x = 1;
y = ++x; // x is now 2, y is also 2
y = x++; // x is now 3, y is 2
這樣看完應該更無懸念了, post-increment 不是等號左邊跑完進行 ++, 也不是等號右邊跑完進行 ++, 就是這一行整個跑完才進行 ++。// Copy one array to another
void copy_array(float *src, float *dst, int n)
{
while (n-- > 0) // Loop that counts down from n to zero
*dst++ = *src++; // Copies element *(src) to *(dst),
// then increments both pointers
}
以上是說明功能上的不同; 在場景上, 陣列或指標的操作很適合 post-increment, 有「同一行, 同時完成操作並與結束往下一個位置移動, 的簡潔代碼」設計 。性能呢 ? Foo( ++x ) 跟 Foo ( x++ ) 各跑 1 千萬次, 結果是
Foo( ++x ) 這個 pre-increment 呼叫版本性能穩定領先, 非常微小的領先。
附帶一提, 就單一行性能來比較 ++x 與 x++, 性能上沒有顯著差異。跑 1000 萬次還看不太出來, 要到 1 億次才互有領先。
所以看來團隊內統一寫法即可。
迴圈操作
針對迴圈內的操作, 這兩個究竟有沒有差別 ?for (int i = 0; i < 5; i++)
for (int i = 0; i < 5; ++i)
結論就是: 沒有差別。 迴圈內都是拿 i 累進前的值來操作。也就是 i 依序是0, 1, 2, 3, 4
在很多語言, 退出迴圈之後還可以拿到區域變數 i, 若拿的到並列印出來, 就會是
5
為了確定大家真的了解, 請試著回答以下迴圈, 每次的 ( n, i ) 值為何 ?
// Test 1 - (n, i) pair of i++ version :
int n = -1;
for (int i = 0; i < 5; n = i++) {
Console.WriteLine(Tuple.Create<int, int>(n, i) + ",");
}
// Test 2 - (n, i) pair of ++i version :
int n = -1;
for (int i = 0; i < 5; n = ++i) {
Console.WriteLine(Tuple.Create<int, int>(n, i)) + ",";
}
以第一個 Test 1 版本來說, 如果- 猜 (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) ... 的, 請到各大線上的基礎程式課程, 用力再複習一次
- 猜 (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) ... 的, 不錯, 這個接近 Test 2 版本的答案
- 不猜 ?
來看答案, 是否跟你想的一樣呢 ?
再用 JavaScript 版本來確認, 是一致的結果,
複習一下好了, The Java™ Tutorials 針對 Loop 的介紹
for (initialization; termination;
increment) {
statement(s)
}
When using this version of the for statement, keep in mind that:For 三段的撰寫格式中,第一段 initialization 只會於一開始被執行一次; 第二段 termination 是判斷跳脫迴圈的判斷式, 每次都會判斷 (每次一開始); 第三段 increment 則會於每次迴圈執行結束後執行 (每次的結尾)。這也解釋了為何一開始 n 是初值 -1 進入迴圈, 而不是 n = ++i 或 n = i++ 的結果進入迴圈。
- The initialization expression initializes the loop; it's executed once, as the loop begins.
- When the termination expression evaluates to false, the loop terminates.
- The increment expression is invoked after each iteration through the loop; it is perfectly acceptable for this expression to increment or decrement a value.
注意 n = i++ 的版本, n 值在第三段 increment 拿到的是尚未 i + 1 的版本 !
運算子多載
有個特例。有些語言如 C++, 具有運算子多載 (operator overloading) 的編程設計。你可以把 ++ 這個運算子用別的操作內容來取代 (你高興的話把 ++ 改為 -- )也可以, 尤其類別物件本來就是要交代 + 或 ++ 在這個類別究竟代表什麼 (如果需要)。假設有個 2 維矩陣類別 Matrix2d, 想要進行矩陣 A++ 或 ++A (矩陣內每個元素值 +1)
A =
x1 y1
x2 y2
A++ =
x1+1 y1+1
x1+1 y1+1
++A =
x1+1 y1+1
x1+1 y1+1
C++ 可能這麼設計
Matrix2d& Matrix2d::operator++() // ++A
{
this.x1 ++;
this.x2 ++;
this.y1 ++;
this.y2 ++;
return *this;
}
Matrix2d Matrix2d::operator++(Matrix2d& value) // A++
{
Matrix2d temp(value);
temp.x1 ++;
temp.x2 ++;
temp.y1 ++;
temp.y2 ++;
return temp;
}
如此一來, 先 ++ 跟後 ++, 就會導致進入不同實作, A++ 產生了無法被編譯器忽略的 temp 變數, 會有成本速度較慢 , 呼叫越多次則兩者性能會越拉越開。參考資料
- https://en.wikipedia.org/wiki/Increment_and_decrement_operators
- https://en.wikipedia.org/wiki/Operator_overloading
- https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
- https://stackoverflow.com/questions/467322/is-there-any-performance-difference-between-i-and-i-in-c
- https://stackoverflow.com/questions/24886/is-there-a-performance-difference-between-i-and-i-in-c
Comments
Post a Comment