答え
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
