forループとrange — 決まった回数を実行する
回数が分かっているときはforループが適切です。rangeの3つの形式、なぜ終わりが除外されるのか、forとwhileの使い分け。
どうやって学びますか?
解決する問題
前の章では、1から5を合計するのに4行かかり、そのうち2行はカウントを維持するためだけのものでした — 開始値と増分です。増分を書き忘れると無限ループになります。
しかし、この種の仕事についてあることに気づきます:何回実行されるかが最初から分かっているのです。5回です。そうであれば、自分で管理する必要はありません — Pythonに任せればいいのです。
それがまさにforループの役割です。カウントが自分の手を離れているため、rangeを使ったforループはそれ自体では無限ループになりません — 繰り返し回数はループが始まる時点で固定されます。これが最大の利点です(例外があります:リストをループしながら同じリストに項目を追加し続けると、ループは終わりません。リストは次の章で扱います)。
この章を終える頃には
forループで決まった回数コードを実行できるrange()の3つの形式すべてを使える- なぜ
range(1, 5)が5個ではなく4個の数を生成するのか説明できる - 文字列の文字をループで処理できる
- 与えられた仕事に
forとwhileのどちらが適しているか判断できる
前提条件: whileループ — 条件が成り立つ間実行する.
最もシンプルな形
for number in range(5):
print(number)0
1
2
3
43行で書けた仕事が1行になり、管理するカウンタはありません。読み方は:「range(5)が生成するそれぞれの数について、ブロックを1回実行する — それぞれの回で、その数をnumberに入れる」です。
すぐに2つのことが分かります:
0から始まり、5は現れません — 合計5個の数ですが、0から4です。これはPythonがあらゆる場所で適用している同じルールです:スライスで終了インデックスが除外されるのと同様に、rangeでも除外されます。
numberという名前はあなたの選択です。 for i in range(5)でもまったく問題なく、iはカウンタの慣習的な名前です。しかし数に意味があるときは、その意味に合わせて名前をつけましょう — for student_number in range(30)ならそれ自体で読めます。
range()の3つの形式
print("--- range(5) ---")
for i in range(5):
print(i, end=" ")
print()
print("--- range(2, 6) ---")
for i in range(2, 6):
print(i, end=" ")
print()
print("--- range(0, 10, 3) ---")
for i in range(0, 10, 3):
print(i, end=" ")
print()
print("--- range(5, 0, -1) ---")
for i in range(5, 0, -1):
print(i, end=" ")
print()--- range(5) ---
0 1 2 3 4
--- range(2, 6) ---
2 3 4 5
--- range(0, 10, 3) ---
0 3 6 9
--- range(5, 0, -1) ---
5 4 3 2 1ルールは以下の通りです:
range(stop)—0から始まり、stopの手前で終わるrange(start, stop)—startから始まり、stopの手前で終わるrange(start, stop, step)— それぞれstepずつ進む;負のstepなら逆方向に数える
最後の2つを確認してください。range(0, 10, 3)は0 3 6 9を生成します — 次は12になりますが、これは10を超えているのでそこで止まります。そしてrange(5, 0, -1)は5 4 3 2 1を生成します — 0は現れません。ここでも終了値は除外されるからです。
なぜ終了値が除外されるのか? 計算が簡単になるからです。range(0, 5)にはいくつ数があるか?5 - 0なので5個です。range(3, 8)なら?8 - 3で5個です。引くだけで個数が分かり、1を足すかどうかで迷いません。そして2つの範囲を並べたとき —range(0, 5)とrange(5, 10)— 何も漏れず、何も重なりません。
1から10までやりたいとき
この間違いは十分に頻繁に起こるので、専用のセクションを設けます:
print("Wrong — stops at 9:")
for i in range(1, 10):
print(i, end=" ")
print()
print("Right — includes 10:")
for i in range(1, 11):
print(i, end=" ")
print()Wrong — stops at 9:
1 2 3 4 5 6 7 8 9
Right — includes 10:
1 2 3 4 5 6 7 8 9 10ルールを1行で言うと:欲しい最後の数より1大きい数をstopに書く。
累積 — 今度は簡単な方法で
total = 0
for number in range(1, 6):
total = total + number
print("Sum of 1 to 5:", total)Sum of 1 to 5: 15同じ仕事をwhileで書いた版と比べてみてください:作るカウンタも、増やすカウンタも、無限ループのリスクもありません。累積変数だけは、前と同じようにループの外側で開始する必要があります。
文字列をループで処理する
forは数値だけでなく、文字列も1文字ずつ処理します:
word = "Python"
for letter in word:
print(letter, end="-")
print()
vowel_count = 0
for letter in "programming":
if letter in "aeiou":
vowel_count = vowel_count + 1
print("Vowels:", vowel_count)P-y-t-h-o-n-
Vowels: 3ここにはrangeがありません。なぜなら不要だからです — for letter in wordが直接文字を処理します。インデックス計算をするよりシンプルで、IndexErrorのリスクもありません。
インデックスと文字の両方が必要なときはenumerate()があります;これはリストの章で扱います。
breakとcontinueもここでは同じように働く
whileのときとまったく同じです:
for number in range(1, 20):
if number % 7 == 0:
print("First multiple of 7:", number)
breakFirst multiple of 7: 7forとwhileの使い分け
1つ質問してください:繰り返し回数がループ開始前に分かっているか?
分かっているなら、for:
- 5回実行する
- 1から100まで進める
- 単語のすべての文字を見る
- リストのすべての項目に何かする
分かっていないなら、while:
- 有効な入力が来るまで尋ね続ける
quitと入力されるまで実行する- ファイルに行が残っている間読み続ける
迷ったらforを選びましょう。 理由はシンプルです:rangeや固定された文字列を使ったforループは確実に終わりますが、whileループはそうとは限りません。どちらでも動くときは、ミスを許さない方を選ぶのが賢明です。
完全な例
multiplication.py:
# Print a multiplication table, then a small summary
number = int(input("Which table? "))
upto = int(input("Up to? "))
print()
print(f"--- Table of {number} ---")
total = 0
for i in range(1, upto + 1):
result = number * i
total = total + result
print(f"{number} x {i:2} = {result:4}")
print()
print(f"Rows : {upto}")
print(f"Sum : {total}")
print(f"Average : {total / upto:.2f}")Which table? 7
Up to? 5
--- Table of 7 ---
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
Rows : 5
Sum : 105
Average : 21.003つ注目してください。第一に、range(1, upto + 1) — uptoまで到達するには1大きく書く必要がありました。第二に、{i:2}と{result:4} — f-stringの中で数値が占める幅を指定でき、これで列が揃います。第三に、totalはループの外で開始し、中で増やします — 前と同じ累積変数です。
ここで1つ質問してみてください:Up to?に0と答えたらどうなるか。range(1, 1)は数を1つも生成しないのでループは一度も回らず — するとtotal / uptoがゼロ除算になります。これは前の章と同じ教訓です:ループが累積したものを使う前に、何かが累積されたかを確認しましょう。
うまくいかないとき
最後の数が抜けている range(1, 10)は9で終わります。stopに1を足しましょう:range(1, 11)。
TypeError: 'float' object cannot be interpreted as an integer range()が受け付けるのは整数(int)だけです。range(2.5)やrange(1, n / 2)は動きません — int()で変換するか、//で割りましょう。
SyntaxError: expected ':' for行の末尾のコロンがありません。
IndentationError: expected an indented block after 'for' statement ループの中の行がインデントされていません。
ループが一度も回らなかった rangeが空です。range(5, 0)は何も生成しません — 下向きに数えるには第3引数が必要です:range(5, 0, -1)。
ループ後もtotalが0のまま total = 0の行がループの中に入り込んでいて、毎回リセットされています。外側に置く必要があります。
ループの中の変数を外で使おうとしたらNameError rangeが空だったためループが一度も回らず、変数が作られませんでした。ループの前に初期値を与えておきましょう。
ステップ 4 / 6 — 予想する
理解度をチェック
このループは何をprintしますか?
for i in range(1, 5):
print(i, end=" ")- A1 2 3 4 5
- B1 2 3
- C0 1 2 3 4
- D1 2 3 4
第3引数がステップを設定します。出力は?
for i in range(0, 10, 3):
print(i, end=" ")- A3 6 9
- B0 3 6 9
- C0 1 2 3 4 5 6 7 8 9
- D0 3 6 9 12
意図は1から10の合計、つまり55です。しかし45が表示されます。なぜ?
total = 0
for number in range(1, 10):
total = total + number
print("Sum of 1 to 10:", total)- A`total`がループの外で初期化されているが、中で行うべきだった
- B`for`ではなく`while`を使えば正解になる
- C`total + number`ではなく`total + i`と書くべきだった
- D`range(1, 10)`は`9`で終わり`10`を含まない — `range(1, 11)`が必要
回答にはアカウントが必要です
ログインして答えを確認
問題は上にあります。頭の中で答えを考えることがいちばん大切です。ログインすると、正解・解説・3 段階のヒントを確認できます。
やってみよう
stars.pyというファイルを書いて、数を受け取って三角形を表示させましょう。入力が5の場合:
*
**
***
****
*****さらに次の3つを追加してください:
- 一番下に星の総数を表示する(5なら15)
- 逆さ三角形も表示する — 上が5個、下が1個
- 偶数行をスキップする版を表示する(
continueを使う)
最初のものは簡単に確認できます — 1から5の合計を手計算して、プログラムの結果と一致するか見てください。そして"*" * iを覚えていますか? 行の星を作るのに、ループの中にさらにループは要りません。