[nico]http://www.nicovideo.jp/watch/1397552685[/nico]
というわけで、「Arduino 日本語リファレンス」(WebArchive)にひどい罠を見つけたので、紹介します。
aの型が書かれていませんが、int型だとすると最初の実装と次の実装でmin関数に「渡されそうな値」が違うことは、Arduino 日本語リファレンス さんが書きました:min()、max()、abs()の各関数は実装の都合により、カッコ内で関数を使ったり変数を操作することができません。たとえば、min(a++, 100)とすると正しい答が得られません。かわりに、次のようにしてください。
a++;
min(a, 100);
賢明な皆さんであればすぐにわかるでしょう。
a++は後置インクリメントなので、インクリメントする前の値が返ります。
しかし、aをmin関数に渡す前にインクリメントしてしまうと、当然インクリメントした後の値が渡されてしまいます。
しかし、普通のC言語では渡されそうな値が違いそうですが、もしかしたらArduinoの言語だと違わないのかもしれません。
念のため実際に検証してみましょう。
void setup() {
Serial.begin(9600);
}
int my_min(int a, int b) {
return a <= b ? a : b;
}
void print_data(int i, int a, int ret) {
Serial.print("i = ");
Serial.print(i, DEC);
Serial.print(", a = ");
Serial.print(a, DEC);
Serial.print(", ret = ");
Serial.print(ret, DEC);
Serial.println();
}
void wrong_usage() {
Serial.println("----- wrong usage");
for (int i = 98; i <= 102; i++) {
int a = i;
int ret;
ret = min(a++, 100);
print_data(i, a, ret);
}
}
void reference_usage() {
Serial.println("----- reference usage");
for (int i = 98; i <= 102; i++) {
int a = i;
int ret;
a++;
ret = min(a, 100);
print_data(i, a, ret);
}
}
void func_usage() {
Serial.println("----- func usage");
for (int i = 98; i <= 102; i++) {
int a = i;
int ret;
ret = my_min(a++, 100);
print_data(i, a, ret);
}
}
void loop() {
switch (Serial.read()) {
case 'w':
wrong_usage();
break;
case 'r':
reference_usage();
break;
case 'f':
func_usage();
break;
}
}
まずはfを送信して「普通」の関数で実装したmy_minについてmy_min(a++, 100)を実行した結果。
----- func usage
i = 98, a = 99, ret = 98
i = 99, a = 100, ret = 99
i = 100, a = 101, ret = 100
i = 101, a = 102, ret = 100
i = 102, a = 103, ret = 100
次に、リファレンスに載っている「間違った」使い方を試してみましょう。wを送信します。
----- wrong usage
i = 98, a = 100, ret = 99
i = 99, a = 101, ret = 100
i = 100, a = 101, ret = 100
i = 101, a = 102, ret = 100
i = 102, a = 103, ret = 100
そして、リファレンスで「推奨された」書き方を試してみましょう。rを送信します。
----- reference usage
i = 98, a = 99, ret = 99
i = 99, a = 100, ret = 100
i = 100, a = 101, ret = 100
i = 101, a = 102, ret = 100
i = 102, a = 103, ret = 100
さらに、原文にも同様の罠が書かれています。
(http://arduino.cc/en/Reference/Min) (WebArchive)Arduino - Min さんが書きました:min(a++, 100); // avoid this - yields incorrect results
a++;
min(a, 100); // use this instead - keep other math outside the function
日本語版にはなさそうですが、max関数、abs関数についてもご丁寧に同様の罠が書かれています。
(http://arduino.cc/en/Reference/Max) (WebArchive)Arduino - Max さんが書きました:max(a--, 0); // avoid this - yields incorrect results
a--; // use this instead -
max(a, 0); // keep other math outside the function
(http://arduino.cc/en/Reference/Abs) (WebArchive)Arduino - Abs さんが書きました:abs(a++); // avoid this - yields incorrect results
a++; // use this instead -
abs(a); // keep other math outside the function
GitHubにもIssueとして投げておきました。
The warnings in reference of min(), max() and abs() seem wrong · Issue #2602 · arduino/Arduino