Python では繰り返しのために for
と while
という構文を用意しています。今回は先ず for
の使い方について勉強したいと思います。
for を使った繰り返しの例
Python の for
には以下に示すように色んな使い方があります。
ただどれも共通しているのは「塊(かたまり)から一つずつ取り出して使っている」という点です。
- 文字列は1文字ずつ繰り返しに使われます
range
は初登場です。使い方は難しくないのですが「0
」から始まることに注意!- 辞書(dict)は、そのままではキー(key)しか取れない
- 「
keys()
」を使うとキー(key)のみを順番に取り出す - 「
values()
」を使うとバリュー(value)のみを順番に取り出す - 「
items()
」を使うと、キー(key)とバリュー(value)をタプル(tuple)で取り出す
さらに「(変数1, 変数2)
」と記載することでキーとバリューを直接、変数に代入できる
- 「
- 集合(set)は取り出す順番がバラバラ
という点が特徴です。
このような使い方ができることを “イテラブル(iterable)” といいます。Python に限らず、プログラム界隈ではよく登場する言葉なので覚えておきましょう。では使い方を実際のプログラムでご紹介します。
print('---------- string') for s in 'abc': print(s) #==> 「a」「b」「c」 print('---------- range') for r in range(3): print(r) #==> 「0」「1」「2」 ※数値で出力 print('---------- list') for l in ['a', 'b', 'c']: print(l) #==> 「a」「b」「c」 print('---------- tuple',) for t in ('a', 'b', 'c'): print(t) #==> 「a」「b」「c」 print('---------- dict') for d in {'a': 1, 'b': 2, 'c': 3}: print(d) #==> 「a」「b」「c」 print('---------- dict with keys()') for k in {'a': 1, 'b': 2, 'c': 3}.keys(): print(k) #==> 「a」「b」「c」 print('---------- dict with values()') for v in {'a': 1, 'b': 2, 'c': 3}.values(): print(v) #==> 「1」「2」「3」 print('---------- dict with items()') for (k, v) in {'a': 1, 'b': 2, 'c': 3}.items(): print(f"{k}: {v}") #==> 「a: 1」「b: 2」「c: 3」 print('---------- set') for s in {'a', 'b', 'c'}: print(s) #==> 「a」「c」「b」や、「c」「a」「b」など、順番はランダム
ちなみに、繰り返されるのは for
の以降で、インデント(字下げ)されている部分になります。スロットゲーム中でも 2 箇所に使われています。
この様に Python はインデント(字下げ)で処理のまとまりを表すという特徴があります。
この内、2つ目の for
はゲーム回数や、メッセージに関わっていることが予想できます。現在 3 回しか挑戦できないスロットゲームを 4回、または 5 回やれるようにするには、どの様に修正したらいいか分かりますでしょうか?ここまでで、皆さんは for
の使い方と、フォーマットを用いた文字列の操作について勉強してきましたので、それらを使って応用することができるはずです。「009.py」を作って、是非、色んなパターンで試してみて下さい。
以下では一部の例を記載いたしました。
# リストの要素を増やすパターン for message in ['1回目', '2回目', '3回目', '4回目', '最後だよ']: print(message) bets = ask_bets(player_coin) marks = show_and_get_result() division = get_division(marks) player_coin = calculate_coin(player_coin, bets, division)
# range を使うパターン(1) # ただし、最後のメッセージは「最後だよ」とはなりません for r in range(5): message = f'{r + 1}回目' print(message) bets = ask_bets(player_coin) marks = show_and_get_result() division = get_division(marks) player_coin = calculate_coin(player_coin, bets, division)
# range を使うパターン(2) game_count = range(5) for r in game_count: message = f'全 {len(game_count)} 回中 {r + 1} 回目の挑戦' print(message) bets = ask_bets(player_coin) marks = show_and_get_result() division = get_division(marks) player_coin = calculate_coin(player_coin, bets, division)
(応用)for と「enumerate」「zip」の合わせ技
応用編になります。スロットゲーム中では使用していないですが、よく登場する形なので予習のつもりで「enumerate()
」「zip()
」の使い方を確認します。enumerate
の方は、対象のリストからzip
の方は、ついつい “圧縮” をイメージしてしまいますが、ジッパー……つまり”合わせて固定する”……方のイメージですね。
print('---------- list with enumerate()') for (i, v) in enumerate(['a', 'b', 'c']): print(f'{i}: {v}') #==> 「0: a」「1: b」「2: c」 print('---------- list with zip()') for (v1, v2, v3) in zip(['a', 'b', 'c'], ['1', '2', '3', '4'], ['!', '?']): print(f'{v1}, {v2}, {v3}') #==> 「a, 1, !」「b, 2, ?」 ※一番短いリストに合わせる
“アンチパターン” 〜for ループ内での文字列結合は非推奨!〜
Avoid using the
3.10 Strings | Google Python Style Guide+
and+=
operators to accumulate a string within a loop.
Google 翻訳の結果は「+
および +=
演算子を使用して、ループ内に文字列を累積することは避けてください。」になります。
「結果よりも過程が大事!」とまでは言いません。しかし、プログラムにも、たとえ同じ結果になったとしても良い書き方と、悪い書き方というものが存在します。良い慣例は「ベストプラクティス」、良くない慣例は「アンチパターン」と呼ばれます。スロットゲーム中には、このアンチパターンが含まれています。内容は上記英語(または、Google 翻訳)の通りで、この書き方をすると処理にかかる負荷が大きくなります。
では、どのようにするのが良いのでしょうか?答えは、引用元に記載されているとおり
add each substring to a list and
3.10 Strings | Google Python Style Guide''.join
the list after the loop terminates
つまり list
型の変数に要素を追加して、ループが終わった後に join
を使って文字列にしなさい……ということです。list
型の変数に要素を追加するには「append()
」を使います。その使い方も合わせてここで覚えて下さい。修正後のプログラムは以下のようになります。
def show_and_get_result(): """スロットの結果(絵柄)を表示し、結果を取得する (This function is to be called to show and get result.) Returns: str: スロットの結果(絵柄) """ result_list = [] for _ in range(3): index = random.randint(0, 9) result = REEL_MARK_LIST[index] result_list.append(result) result_all = ''.join(result_list) print(result_all) return result_all
他にも文字列にはなるべくシングルクォーテーション「'
」を使うようにとの記載もあります。スロットゲーム中ではそのようにしています。
ここまでで、最終的に以下のようなプログラムになっているかと思います。ただし 151 行目辺りの for
の書き方は、人によって違いますので適宜、読み替えて下さい。
"""スロットゲーム 簡単なスロットゲームです。ゲームスタート時に保有しているコインを増やしましょう。 掛けコイン数を入力すると、スロットの結果が表示されます。 絵柄が揃うと、そのパターンに応じた配当倍率でコインを獲得することができます。 """ import random # "乱数" を生成するために必要なライブラリをメモリ上に読み込む(標準ライブラリ) # スロット(リール)の中身を "定数" で定義する # 配列のインデックスは 0 から始まるので、0 〜 9 番目に格納されている # 定数は全て大文字で記載する REEL_MARK_LIST = ('♠', '♥', '♥', '◆', '◆', '◆', '♣', '♣', '♣', '♣') # 特別なプレーヤーの名前と、その名前を使用した際のボーナスコイン数 BONUS_PLAYER_AND_COINS = {'king': 100, 'queen': 150} # スペシャルサンクスの対象者名 # ボーナスコインは一律 50 枚とする SPECIAL_THANKS = {'akira', # 弊社社長。詳細は社長が作成最多ブログ記事のプロフィール欄をご確認下さい。 'hurry', # 詳細はプロフィール欄から twitter をご確認下さい。 'shachi', # https://shachi-web.com/ } SPECIAL_THANKS_COIN = 50 # ============================================================ # ゲームに必要な関数を定義する # ============================================================ def show_start_message(): """ゲーム開始のメッセージを表示する (This function is to be called to show the start message.) """ print('--------------') print('スロットゲーム') print('--------------') def ask_player_name(): """プレーヤーの名前を尋ねる (This function is to be called to ask player's name.) プレーヤーの名前を入力(input)してもらい、挨拶を表示(print)する 入力されたプレーヤーの名前を返す(return) Returns: str: プレーヤーの名前 """ player_name = input('あなたの名前を入力して下さい: ') print(f'こんにちは {player_name} さん') return player_name def ask_bets(player_coin): """現在の所持コイン数を表示した後、掛けるコイン数(bets)を尋ねる (This function is to be called to ask bets.) 所持コイン数(player_coin)は数値なので、文字列に含めたい場合は文字列(string)に変換する必要がある ==> str() 関数 input() 関数で入力した値は文字列(string)になるので整数(integer)に変換する ==> int() 関数 Args: player_coin (int): プレーヤーの所持コイン数 Returns: int: 掛けコイン数 """ print('------------------------------') print(f'現在の所持コイン数は {player_coin:,} 枚です。') str_bets = input('掛けコイン数を入力して下さい: ') bets = int(str_bets) return bets def show_and_get_result(): """スロットの結果(絵柄)を表示し、結果を取得する (This function is to be called to show and get result.) Returns: str: スロットの結果(絵柄) """ # TODO(佐藤宏行): ループ内で文字列を結合するのは避ける。代わりにリストに貯めて、最後に結合(join)すること # 参照: https://google.github.io/styleguide/pyguide.html#310-strings result_all = '' for i in range(3): index = random.randint(0, 9) result = REEL_MARK_LIST[index] result_all = result_all + result print(result_all) return result_all def get_division(marks): """絵柄に応じた配当倍率を取得する (This function is to be called to get division.) Args: marks (str): スロットの結果(絵柄) Returns: int: 配当倍率 """ if marks == '♠♠♠': print('超大当たり!!') return 15 elif marks == '♥♥♥': print('大当たり!') return 12 elif marks == '◆◆◆': print('大当たり!') return 10 elif marks == '♣♣♣': print('大当たり!') return 8 elif marks[0:2] == '♠♠': print('当たり!') return 5 else: print('ハズレ...') return -1 def calculate_coin(coin, bets, division): """掛けたコイン数(bets)と、配当倍率(division)を使用してプレーヤーの所持コイン数を精算する (This function is to be called to calculate (players's) coin.) Args: coin (int): プレーヤーの精算前の所持コイン数 bets (int): 掛けコイン数 division (int): 配当倍率 Returns: int: 精算後のプレーヤーの所持コイン数 """ coin = coin + bets * division return coin # ============================================================ # 実際にゲームを実行する # ============================================================ # プレーヤーの所持コイン数 player_coin = 123 show_start_message() player_name = ask_player_name() if player_name in BONUS_PLAYER_AND_COINS: player_coin = player_coin + BONUS_PLAYER_AND_COINS[player_name] elif player_name in SPECIAL_THANKS: player_coin = player_coin + SPECIAL_THANKS_COIN for r in range(5): message = f'{r + 1}回目' print(message) bets = ask_bets(player_coin) marks = show_and_get_result() division = get_division(marks) player_coin = calculate_coin(player_coin, bets, division) print('終了です。{} さんの所持コイン数は...'.format(player_name)) print(f'最終的に {player_coin:,} 枚になりました!')
(余談)なぜ “for” なのか……?
繰り返しには「repeat」、ループには「loop」という単語があるのに、何故 for
という単語を使っているのか?折角なので調べてみました。
しかし、なかなか答えが見つからない!多分、私の英語力が足りないせい……。結局、下記 2 つのメッセージにたどり着きました。
つまりは”A for loop statement repeats for a certain number of times.”のforでした。
For文の”For”って何だろう?という疑問 | Unityでインディゲーム道!
The loop body is executed “for” the given values of the loop variable
For loop
1つ目は一定の回数の間……つまり “期間” を表すものとして、2つ目は与えられた値……つまり “対象” を表すものとして「for
」を使用しているということです。Python の構文的には2つ目の使い方の方がしっくりくるかな〜、JavaScript など他のプログラミング言語では1つ目の使い方がしっくりくるかな〜という感じです。皆さんはどう感じますか?
コメント