答え
p
がループ変数になっているので、こう書くのが正解!
int SumListValues(const ListNode *pNode) {
int sum = 0;
for (const ListNode *p = pNode; p; p = p->pNext) {
sum += p->value;
}
return sum;
}
解説
今回は、「ループ変数」を見極める練習問題でした。ループ変数とは、ループが継続する条件となる変数のことです。その値が一定の条件を満たす間だけ、値を更新しながら処理を繰り返します。
もっとも分かりやすいループ変数は、次のようなものでしょう。
for (int i=0; i<10; i++) {
……
}
ここでのループ変数はi
です。条件(10より小さい)を満たす間だけ、値を更新(1ずつ増加)しながら処理を繰り返しています。
では、これから書き直そうとしているwhile
ループを確認してみましょう。
int SumListValues(const ListNode *pNode) {
const ListNode *p = pNode;
int sum = 0;
while (p) {
sum += p->value;
p = p->pNext;
}
return sum;
}
while
の括弧のところを見れば分かりそうだなー。
while (p) {
……
}
p
じゃないですか?
for
に書き換えるかってことなのだけど……
while
とfor
の関係性を理解するのがいいわね。
for
は、while
でループする際のよくあるパターンを書きやすく拡張したようなものです。多くの場合、while
ループは次のような形をしています。
ループ変数を初期化;
while (ループ条件をチェック) {
……
ループ変数を更新;
}
これをfor
ループで表現すると、次のようになります。
for (ループ変数を初期化; ループ条件をチェック; ループ変数を更新) {
……
}
このように、ループ変数に関する記述をまとめられるのがfor
の便利なところです。うまく使えば、プログラムを読みやすくできるでしょう。
for
ループは……
for (int i=0; i<10; i++) {
……
}
while
ループの変形だったのか!
int i=0;
while (i < 10) {
……
i++;
}
この考え方を、問題のwhile
ループにあてはめてみましょう。ループ変数がp
だということは、すでに分かっています。したがって、「ループ変数を初期化」「ループ条件をチェック」「ループ変数を更新」という3つの処理は、それぞれ次の部分だといえます。
int SumListValues(const ListNode *pNode) {
const ListNode *p = pNode; /* ← ループ変数を初期化 */
int sum = 0;
while (p) { /* ← ループ条件をチェック */
sum += p->value;
p = p->pNext; /* ← ループ変数を更新 */
}
return sum;
}
for
に変形できるんじゃないかしら?
for
の書き方に合わせて……っと。うーん、これでいいのかなぁ……
int SumListValues(const ListNode *pNode) {
int sum = 0;
for (const ListNode *p = pNode; p; p = p->pNext) {
sum += p->value;
}
return sum;
}
for
の「ループ条件をチェック」のところが、これでいいのか自信がなかったんです。
p
とだけ書いたところね。これは条件式なのよ。while
やif
なんかの括弧の中と同じ書き方ね。
i<10
みたいな書き方しかしたことがなかったのかしら?
for
は範囲が決まってるときに使うループじゃないですか。
for
もwhile
と同じで、どんなループに使ってもいいの。ただ、あらかじめ範囲が決まっているループはfor
を使うと書きやすいっていうことね。
さて、ここまではfor
ループのおすすめの書き方を紹介しました。でも、ただwhile
をfor
に書き換えるというだけなら、別の方法もあります。
例えば、無限ループとbreak
を組み合わせた、次のような書き方が考えられます。
ループ変数を初期化;
for (;;) { /* ← 無限ループ */
if (ループ条件をチェック) {
break;
}
……
ループ変数を更新;
}
これは、「条件を満たす間だけ処理を繰り返す」のではなく、「条件を満たした時点で処理を終える」ということですね。そのため、「ループ条件をチェック」の部分に入る式は、while
の場合の否定形になります。
今回の問題にあてはめると、次のようになるでしょう。
int SumListValues(const ListNode *pNode) {
const ListNode *p = pNode;
int sum = 0;
for (;;) {
if (p == 0) {
break;
}
sum += p->value;
p = p->pNext;
}
return sum;
}
while
より、行数が増えちゃいましたね。
break
の前後どちらにも処理が必要なときは、無限ループが便利ね。
for (;;) {
前半の処理:
if (ループ条件をチェック) {
break;
}
後半の処理;
}
では、とにかく行数を減らすことを優先させたらどうなるでしょうか?例えば、次のように書くことも可能でしょう。
int SumListValues(const ListNode *pNode) {
int sum = 0;
for (const ListNode *p = pNode; p; sum += p->value, p = p->pNext);
return sum;
}
修正後のプログラム
#include <stdio.h>
#include <stdlib.h>
typedef struct ListNode {
int value;
struct ListNode *pNext;
} ListNode;
int SumListValues(const ListNode *pNode) {
int sum = 0;
for (const ListNode *p = pNode; p; p = p->pNext) {
sum += p->value;
}
return sum;
}
int main(void) {
ListNode node4 = { 40, 0 };
ListNode node3 = { 30, &node4 };
ListNode node2 = { 20, &node3 };
ListNode node1 = { 10, &node2 };
printf("Sum: %d\n", SumListValues(&node1));
return EXIT_SUCCESS;
}
Sum: 100