Saya's Blog

プログラミング歴ゼロの31歳ジャニヲタ、Pythonを学ぶ。超初心者向けの練習問題を5問紹介。

こんにちは!ジャニヲタ歴20年、アクセサリーデザイナーのSayaと申します。

いまから始めてみればいいじゃない!Let’s get on!Let’s get on yea!ということで、このたび齢31歳にして、プログラミングの勉強を知識ゼロからはじめてみました。

今回は世界平和とジャニヲタの社会的地位向上に貢献すべく、私がプログラミングを学ぶ中で得た知見を、ジャニヲタらしく(?)セクシーにシェアしたいと思います。

プログラミング歴ゼロの31歳ジャニヲタ、Pythonを学ぶ

最初にどのプログラミング言語を学べば良いのか迷ったので、とりあえず人気投票をチェック(ジャニヲタあるある)。するとPythonという言語が晴れて今年、プログラミング言語人気ランキング一位を獲得したようなので、Pythonを学ぶことに決めました。

次にPythonを日本語で学ぶか英語で学ぶか迷ったのですが、中学生のころアメリカのHANSONというバンドにハマり、アメリカのファンの方たちとチャットをした経験から、私は英語には抵抗がないので、どうせならと英語で学ぶことに決めました。

プログラマーの友人に英語圏で最も人気のPython教材は何か聞いたところ、「Learn Python 3 the Hard Way」という入門書が1200万人と、嵐の通算コンサート動員数並みの人数に読まれたらしく、これを使って学ぶことに。

超初心者向けの練習問題を5問紹介

入門書を3分の2くらい読み進めたのはいいものの、本当にスキルが身についているか不安だったので、プログラマーの友人に練習問題をいくつか出してもらいました。苦戦の末に全問解いたあと、友人から「問題と解き方をブログに書いてみるといいよ」と薦められたので、ここで書いてみることにします。

練習問題は5問。全問ジャニーズにちなんだ問題です。想定読者は以下の条件に当てはまる方々です。

実行環境はPython 3.6ですが、Python以外の言語を学んでいる方々にも役立てばいいなと思います。

私のような超初心者向けに分かりやすさを重視して書いているので、冗長なのはご理解ください。

目次

  1. メンバーの平均年齢を求める
  2. 最年長のメンバーが誰か調べる
  3. 2番目に最年長のメンバーが誰か調べる
  4. あるメンバーの次に年上のメンバーが誰か調べる
  5. 数年後のメンバーの年齢を正しく求める

練習問題その1: メンバーの平均年齢を求める

まずはウォーミングアップとして、You are my SOUL!SOUL!いつもすぐそばにある!今をときめくジャニーズの財務基盤、嵐先生の平均年齢を、Pythonを使って求めてみましょう。

嵐のメンバーのニックネーム・フルネームと2017年10月現在の年齢はこちらです。まさかご存知ない方はいないと思いますが、念のため。順番は適当です。

ニックネーム フルネーム 年齢
翔くん 櫻井 翔 35
相葉ちゃん 相葉 雅紀 34
にの 二宮 和也 34
大ちゃん 大野 智 36
潤くん 松本 潤 34

翔くんは11月で36歳になりますね!翔くんおめでとう!!(若干のフライング)

データをPythonで表現

上の表をPythonで表現してみると、こうなります。

[
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]

「A, B, C」といったデータの羅列をPythonで表現する際、

という決まりがあるので、「ニックネーム、フルネーム、年齢」は違う系統のデータのためタプルで表現し、そのタプルの羅列は同じ系統のデータのためリストで表現しています。

練習問題その1

上記のようなリストを入力すると、全員の平均年齢を出力するプログラムを書いてみてください。

具体的には、下のaverage_age関数を実装してください。嵐のメンバーの場合、平均年齢は(35+34+34+36+34)/5 = 34.6歳なので、34.6と出力されればOKです。

def average_age(members):
    # ???

# 34.6と表示されればOK
print(average_age([
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]))

以下、解き方をstep by stepブッ飛ぶよりも裸のまま突っ込めで説明していきます。

Step1: メンバーの年齢の合計値を計算する

メンバーの平均年齢を出すには、まずは全員のメンバーの年齢を合計します。仮にmembers変数が以下だったとすると、

members = [
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]

年齢はリストmembers内のタプルの[2]の位置に入っているので、以下のようにすれば合計を求められます。

members[0][2] + \
members[1][2] + \
members[2][2] + \
members[3][2] + \
members[4][2]

(読みやすさのため複数行に分けて書いているので、その際に必要なバックスラッシュ \ を使っています)

しかし、これではメンバーが5人より多い、または少ないグループの平均年齢を求めることはできません。

上の式をよく見ると、「リストmembersに入っている要素を順にとってきて、それぞれの[2]の要素を足す」という振り付けが繰り返し行われています。

というわけで、forループを使うのが良さそうです。足し算の結果をsum_member_ageという変数に保存することにします。

def average_age(members):
    # 1. リスト`members`の要素をとってきて
    for next_member in members:
        # 2. その中の`[2]`の要素を足し、
        #    足し算で得られた結果を変数に保存
        sum_member_age = sum_member_age + next_member[2]

しかし、これを実行すると「sum_member_ageが定義されていない」というエラーが出ます。そこで、ループ処理を行う前にsum_member_ageに初期値0を代入しておくことにします。

def average_age(members):
    # 初期値
    sum_member_age = 0
    for next_member in members:
        sum_member_age = sum_member_age + next_member[2]

Step2: メンバーの年齢の合計値をメンバーの人数で割る

ここまでで年齢の合計値を得ることができたので、次に合計した値をメンバーの数で割って平均値を求めます。

「メンバーの数」はリストmembersに入っている要素の個数と同じなのでlen(members)で得ることができます。つまり実行したい計算式は以下の通り。

sum_member_age / len(members)

これをreturnを使って関数の最後で返すようにします。

def average_age(members):
    sum_member_age = 0
    for next_member in members:
        sum_member_age = sum_member_age + next_member[2]
    return sum_member_age / len(members)

完成です!

print(average_age([
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]))

さっそく嵐のデータを使い、Pythonで実行してみると…

❯ python3.6 average_age.py
34.6

正しい平均年齢が表示されました!嵐もあと5年ほどで40代なのですね・・・

以上、練習問題その1でした。

練習問題その2: 最年長のメンバーが誰か調べる

では次に、練習問題その1と同じリストを使って、最年長のメンバーが誰かを出力するプログラムを書いてみてください。

実装するのは下のoldest_member関数です。嵐のメンバーの場合、最年長は36 歳の大野くんなので、ジャニヲタの愛と親しみを込めてニックネーム”大ちゃん”と出力されればOKです。

def oldest_member(members):
    # ???

# "大ちゃん"と出力されればOK
print(oldest_member([
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]))

Step1: 暫定の最年長のメンバーの年齡を保存する

リスト内の最年長のメンバーが誰か調べるには、リストを一番目から順に調べていき、 「今まで調べた中で最年長のメンバーは誰か」をその都度更新する必要があります。最後まで調べたときに最年長だったメンバーが答えというわけです。

ここでもforループが使えそうです。

年齡を比較できるように、暫定の最年長のメンバーの年齡をoldest_ageという変数に保存することにします。これには初期値として0を代入しておきます。

def oldest_member(members):
    # 暫定の最年長のメンバーの年齡
    oldest_age = 0 # 初期値

    for next_member in members:
        # `oldest_age`を更新していく

Step2: 最年長のメンバーの年齡を更新する

次に、forループの中の「oldest_ageを更新していく」処理を考えます。

ここでは

という処理を最後のメンバーまで繰り返し行います。

def oldest_member(members):
    oldest_age = 0

    for next_member in members:
        # `[2]`に入っている年齢を`oldest_age`と比較する
        if next_member[2] > oldest_age:
            # `[2]`に入っている年齡の方が`oldest_age`
            # より大きければ、`oldest_age`を更新
            oldest_age = next_member[2]

Step3: 最年長のメンバーのニックネームを返す

ここまでの処理で最年長のメンバーの年齡を得ることができました!が、最終的に得たい答えは「最年長のメンバーのニックネーム」です。

そこで、最年長のメンバーのニックネームをoldest_member_nicknameという変数に保存し、oldest_ageを更新した時に、同時に更新することにします。初期値として、空の文字列を定義しておきます。

また、returnを使って、すべてのメンバーの比較が完了した時点でのoldest_member_nicknameを関数の最後で返すようにします。

def oldest_member(members):
    oldest_age = 0
    # 暫定の最年長のメンバーのニックネーム
    oldest_member_nickname = "" #初期値

    for next_member in members:
        if next_member[2] > oldest_age:
            oldest_age = next_member[2]
            # `oldest_age`が更新された時に、
            # `[0]`に入っているニックネームに更新
            oldest_member_nickname = next_member[0]

    # 最終的な最年長のメンバーのニックネームを返す
    return oldest_member_nickname

完成です💕

print(oldest_member([
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]))

嵐のデータを使い、Pythonで実行してみると…

❯ python3.6 oldest_member.py
大ちゃん

「嵐の最年長メンバーがリーダー大ちゃんである」というジャニヲタ的常識を、プログラミングを使って導き出すことができました😏💙

練習問題その3: 2番目に最年長のメンバーが誰か調べる

次は少しディープな世界に入ります。同じリストを使って、2番目に最年長のメンバーが誰かを出力するプログラムを書いてみてください。

実装するのは下のsecond_oldest_member関数です。嵐のメンバーの場合、大野くんの次に最年長のメンバーはしっかり者な「嵐の次男」的存在、35 歳の櫻井くんなので、翔くんと出力されればOKです。

def second_oldest_member(members):
    # ???

# "翔くん"と出力されればOK
print(oldest_member([
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]))

step1: 暫定の最年長/2番目に最年長のメンバーを保存する

ここでも練習問題その2と同じようにforループを使ってリストを一番目から順に調べていく必要がありますが、「今まで調べた中で最年長のメンバーは誰か」に加えて、「今まで調べた中で2番目に最年長のメンバーは誰か」をその都度更新する必要があります。最後まで調べたときに2番目に最年長だったメンバーが答えというわけです。

暫定の最年長のメンバーの年齡oldest_ageに加えて、暫定の2番目に最年長のメンバーの年齢をsecond_oldest_ageという変数に保存し、それぞれに初期値として0を代入しておくことにします。

また同様に、暫定の最年長メンバーのニックネームoldest_member_nicknameに加えて、2番目に最年長のメンバーのニックネームをsecond_oldest_member_nicknameという変数に保存し、それぞれに初期値として空の文字列を定義しておきます。

また、returnを使って、すべてのメンバーの比較が完了した時点でのsecond_oldest_member_nicknameを関数の最後で返すようにします。

def second_oldest_member(members):
    # 暫定の最年長のメンバーの年齡
    oldest_age = 0 # 初期値
    # 暫定の2番目に最年長のメンバーの年齡
    second_oldest_age = 0 # 初期値

    # 暫定の最年長のメンバーのニックネーム
    oldest_member_nickname = "" # 初期値
    # 暫定の2番目に最年長のメンバーのニックネーム
    second_oldest_member_nickname = "" # 初期値

    for next_member in members:
        # `oldest_age`/`second_oldest_age`
        # `oldest_member_nickname`/`second_oldest_member_nickname`
        # を更新していく

    # 最終的な2番目に最年長のメンバーのニックネームを返す
    return second_oldest_member_nickname

Step2: 最年長/2番目に最年長のメンバーの年齡を更新する

次に、forループの中の、oldest_agesecond_oldest_ageを更新していく処理を考えます。

ここでは練習問題その2で行った「リストからとってきた年齡をoldest_ageと比較し、更新する」という振り付けに加えて、さらに「リストからとってきた年齡をsecond_oldest_ageと比較し、更新する」という振り付けを追加する必要があります。

step by stepブッ飛ぶよりも裸のまま〜(以下省略)で考えていきます。

Step2-1: メンバーの年齡が、最年長のメンバーより大きい場合

ここまでは練習問題その2と全く同じですが、この時点で、それまでoldest_ageに入っていた年齡は2番目に最年長の年齡ということになるので、さらに

という処理を追加します。

def second_oldest_member(members):
    oldest_age = 0
    second_oldest_age = 0

    oldest_member_nickname = ""
    second_oldest_member_nickname = ""

    for next_member in members:
        # `[2]`に入っている年齢を`oldest_age`と比較する
        if oldest_age < next_member[2]:
            # `[2]`に入っている年齡の方が`oldest_age`より大きければ、
            # 1. `oldest_age`を更新
            oldest_age = next_member[2]
            # 2. `second_oldest_age`を更新
            second_oldest_age = oldest_age

    return second_oldest_member_nickname

しかしここで少し考えてみましょう🤔

「# 1. 」のステップで oldest_ageを更新すると、oldest_ageそれまでのoldest_ageから[2]の年齡に置き換わります。この状態で「#2. 」のステップを踏むと、second_oldest_age更新された後のoldest_age、つまり[2]の年齡にはばまれ、それまでのoldest_ageに置き換えることができなくなってしまいます。まさにゆずれないよ誰もじゃまできない状態です。

そこで処理の順序を入れ替え、先にsecond_oldest_ageを更新してから、oldest_ageを更新することにします。

def second_oldest_member(members):
    oldest_age = 0
    second_oldest_age = 0

    oldest_member_nickname = ""
    second_oldest_member_nickname = ""

    for next_member in members:
        # `[2]`に入っている年齢を`oldest_age`と比較する
        if oldest_age < next_member[2]:
            # `[2]`に入っている年齡の方が`oldest_age`
            # より大きければ、
            # 1. `second_oldest_age`を更新
            second_oldest_age = oldest_age
            # 2. `oldest_age`を更新
            oldest_age = next_member[2]

    return second_oldest_member_nickname

これで問題なく2つの変数を更新することができるようになりました!

Step2-2: メンバーの年齡が、最年長のメンバーと同じかそれより小さい場合

では、「比較した結果[2]に入っている年齡がoldest_ageと同じか、oldest_ageより小さい場合」を考えてみます。

この場合、oldest_ageを更新する必要はありません。

second_oldest_ageはどうでしょうか。ここで[2]の年齡とsecond_oldest_ageの比較が必要になります。

つまり、

という処理を追加します。

def second_oldest_member(members):
    oldest_age = 0
    second_oldest_age = 0

    oldest_member_nickname = ""
    second_oldest_member_nickname = ""

    for next_member in members:
        if next_member[2] > oldest_age:
            second_oldest_age = oldest_age
            oldest_age = next_member[2]

        # `[2]`に入っている年齢が
        # `oldest_age`と同じかそれより小さい場合
        # `[2]`に入っている年齢を
        # `second_oldest_age`と比較する
        elif next_member[2] <= oldest_age and \
        next_member[2] > second_oldest_age:
            # `[2]`に入っている年齡の方が`second_oldest_age`より大きければ、
            # `second_oldest_age`を更新
            second_oldest_age = next_member[2]

    return second_oldest_member_nickname

これで、2番目に最年長のメンバーの年齡が分かりました!

Step3: 最年長/2番目に最年長のメンバーのニックネームを更新する

最終的に得たい答えは2番目に最年長のメンバーのニックネームなので、練習問題その2と同様、oldest_agesecond_oldest_ageを更新した時に、oldest_member_nicknamesecond_oldest_member_nickname を同時に更新することにします。

ここでも年齡と同様、更新の順序に注意です!

def second_oldest_member(members):
    oldest_age = 0
    second_oldest_age = 0

    oldest_member_nickname = ""
    second_oldest_member_nickname = ""

    for next_member in members:
        if next_member[2] > oldest_age:
            second_oldest_age = oldest_age
            oldest_age = next_member[2]
            # `second_oldest_age`が更新された時に、
            # `second_oldest_member_nickname`を
            # それまでの最年長のメンバーのニックネームに更新
            second_oldest_member_nickname = oldest_member_nickname
            # `oldest_age`が更新された時に、
            # `oldest_member_nickname`を
            # `[0]`に入っているニックネームに更新
            oldest_member_nickname = next_member[0]

        elif next_member[2] <= oldest_age and next_member[2] > second_oldest_age:
            second_oldest_age = next_member[2]
            # `second_oldest_age`が更新された時に、
            # `second_oldest_member_nickname`を
            # `[0]`に入っているニックネームに更新
            second_oldest_member_nickname = next_member[0]

    return second_oldest_member_nickname

完成です💕💕

print(oldest_member([
    ("翔くん", "櫻井翔", 35),
    ("相葉ちゃん", "相葉雅紀", 34),
    ("にの", "二宮和也", 34),
    ("大ちゃん", "大野智", 36),
    ("潤くん", "松本潤", 34)
]))

嵐のデータを使い、Pythonで実行してみると…

❯ python3.6 second_oldest_member.py
翔くん

リーダーの長男・大野くんを、しっかり者の次男・櫻井くんが支えていることが、プログラミングでも証明されました😉💙❤️

大野くんと櫻井くんの年長コンビは、漢字の「嵐」の上の部分をとって「山コンビ」と呼ぶという大切なことを最後に付け加えておきます(ジャニヲタ豆知識)。

練習問題その4: あるメンバーの次に年上のメンバーが誰か調べる

さらにディープな世界に入ります。次は「あるメンバーの次に年上のメンバーが誰かを出力するプログラムを書いてください。」という問題です。

嵐の「山コンビ」涙を禁じ得ない数々のエピソードを生み出してきたように、そして櫻井くんとその次に年長の相葉くんの「櫻葉コンビ」笑いを堪えきれない数々の珍事を生み出してきたように、グループの萌えポイントターニングポイントにはいつも「年の近いメンバー同士のコンビ」の存在があります。

刻々と紡がれるジャニーズヒストリーの中で重要な事件を見逃さないよう、このコンビを各グループごとに把握しておくことはジャニヲタの必須要件です。

嵐についてはすでに多くの事件が詳細な記録とともに残されているので、ここでは東京オリンピックの頃にはきっと時代が追いついているであろう、ポテンシャルを秘めまくったジャニーズの次世代グループ「Sexy Zone」のメンバーを使って、コードとともに未知の世界を解明することにします。

Sexy Zoneのメンバーのニックネーム・フルネームと2017年10月現在の年齢はこちらです。さあ、未知の世界「Sexy世代」へ🥀

ニックネーム フルネーム 年齢
聡ちゃん 松島 聡 19
マリ マリウス 葉 17
ケンティー 中島 健人 23
風磨くん 菊池 風磨 22
勝利くん 佐藤 勝利 20

上の表をPythonで表現してみると、こうなります。

[
    ("聡ちゃん", "松島聡", 19),
    ("マリ", "マリウス葉", 17),
    ("ケンティー", "中島健人", 23),
    ("風磨くん", "菊池風磨", 22),
    ("勝利くん", "佐藤勝利", 20)
]

next_older_member関数

実装するのは下のnext_older_member関数です。対象のメンバーmembersに加え、「あるメンバー」のニックネームを指定できるようnicknameという変数を追加します。

たとえばnickname"風磨くん"と指定した場合、22歳の菊池風磨くんの次に年上のケンティーと出力されればOKです。

# 変数`nickname`を追加
def next_older_member(members, nickname):
    # ???

# "ケンティー"と出力されればOK
print(next_older_member([
    ("聡ちゃん", "松島聡", 19),
    ("マリ", "マリウス葉", 17),
    ("ケンティー", "中島健人", 23),
    ("風磨くん", "菊池風磨", 22),
    ("勝利くん", "佐藤勝利", 20)
], "風磨くん"))

Step1: 「あるメンバー」の年齡を保存する

まずは、nicknameに指定された「あるメンバー」の年齡を求める必要があります。後にこの年齡と各メンバーの年齡を比較していくので、これをcompared_ageという変数に保存し、初期値0を代入しておきます。

forループを使って、リストmembers[0]に入っているニックネームがnicknameと一致した時に、compared_agemembers[2]に入っている年齡に更新する処理を行います。

def next_older_member(members, nickname):
    # `nickname`に指定されたメンバーの年齡
    compared_age = 0 # 初期値

    # `members`のニックネームが`nickname`と一致した時に
    # `compared_age`を`members`の年齡に更新
    for next_member in members:
        if next_member[0] == nickname:
            compared_age = next_member[2]

compared_ageが正しく指定されているかどうか、printを使って確かめてみましょう。上記の最後にprintを追加し、nickname"風磨くん"と指定して実行してみます。

def next_older_member(members, nickname):
    # ...

    print(compared_age)

print(next_older_member([
    ("聡ちゃん", "松島聡", 19),
    ("マリ", "マリウス葉", 17),
    ("ケンティー", "中島健人", 23),
    ("風磨くん", "菊池風磨", 22),
    ("勝利くん", "佐藤勝利", 20)
], "風磨くん"))

実行すると、風磨くんの年齡である22が出力されました。

❯ python3.6 next_older_member.py
22

これで「あるメンバー」の年齡をcompared_ageに保存することができました!

次のステップに進む前に、printは消しておきます。

Step2: 「次に年上のメンバー」を保存する

次に、これまでの練習問題と同じようにforループを使って、各メンバーの年齡をcompared_ageと順に比較していく処理を考えます。

求めたい「次に年上のメンバー」の年齡を変数next_older_member_ageに、ニックネームを変数next_older_member_nicknameに保存し、それぞれに初期値0と空の文字列を代入しておくことにします。

また、ループ処理の中で条件が満たされた時にnext_older_member_ageを今調べているメンバーの年齡に更新し、同時にnext_older_member_nicknameを今調べているメンバーのニックネームに更新します。

さらに、returnを使って、すべてのメンバーの比較が完了した時点でのnext_older_member_nicknameを関数の最後で返すようにします。

def next_older_member(members, nickname):
    # ... (compared_ageを指定、省略)

    # 「次に年上のメンバー」の年齡
    next_older_member_age = 0 # 初期値
    # 「次に年上のメンバー」のニックネーム
    next_older_member_nickname = "" #初期値

    for next_member in members:
        # 条件が満たされた時に
        if # ???:
            # `next_older_member_age`を更新
            next_older_member_age = next_member[2]
            # `next_older_member_nickname`を更新
            next_older_member_nickname = next_member[0]

    # 最終的な「次に年上のメンバー」のニックネームを返す
    return next_older_member_nickname

Step3: 「次に年上のメンバー」を更新する

ではnext_older_member_ageを更新するのは、どんな条件がそろった時でしょうか。

まず、「次に年上のメンバー」の年齡は必ず「あるメンバー」の年齡より大きいので、

1. members[2]に入っている年齡がcompared_ageより大きい

という条件が必要です。

さらに、その時点での「次に年上のメンバー」と今調べているメンバーの年齡を比較し、その時点での「次に年上のメンバー」の方が大きければ、「次に年上のメンバー」は今調べているメンバーに置き換わることになるので

2. next_older_member_agemembers[2]に入っている年齡より大きい

という条件も同時に満たす必要があります。

今調べているメンバーの年齡が、その時点での「次に年上のメンバー」の年齡と、「あるメンバー」の年齡の間にある時に、next_older_member_ageを更新する、というわけです😉

def next_older_member(members, nickname):
    # ...

    next_older_member_age = 0
    next_older_member_nickname = ""

    for next_member in members:
        # 1. `members`の`[2]`に入っている年齡が
        # `compared_age`より大きく
        # 2. `next_older_member_age`が
        # ` members`の`[2]`に入っている年齡より大きい場合
        if next_member[2] > compared_age and \
        next_older_member_age > next_member[2]:
            # `next_older_member_age`を更新する
            next_older_member_age = next_member[2]
            next_older_member_nickname = next_member[0]

    return next_older_member_nickname

さっそく実行してみましょう!

# "ケンティー"と出力されればOK
print(next_older_member([
    ("聡ちゃん", "松島聡", 19),
    ("マリ", "マリウス葉", 17),
    ("ケンティー", "中島健人", 23),
    ("風磨くん", "菊池風磨", 22),
    ("勝利くん", "佐藤勝利", 20)
], "風磨くん"))

しかし、実行してみると、出力されたのは空の文字列だけ。どこが間違っているのでしょうか?

❯ python3.6 next_older_member.py



Step4: next_older_member_ageのバグを直す

def next_older_member(members, nickname):
    # ...

    next_older_member_age = 0
    next_older_member_nickname = ""

    for next_member in members:
        if next_member[2] > compared_age and \
        next_older_member_age > next_member[2]:
            next_older_member_age = next_member[2]
            next_older_member_nickname = next_member[0]

    return next_older_member_nickname

上記のforの中身について、少し考えてみましょう🤔大人の最初にきめたやり方それが正解なの?

最初に定義しておいたnext_older_member_ageの初期値0はどのメンバーよりも年上ではありません。これでは、2番目のforループの中にある、

if next_member[2] > compared_age and \
    next_older_member_age > next_member[2]:

上の二行目の「next_older_member_agemembers[2]に入っている年齡より大きい」という条件は未来永劫、満たされることはありません。

だから、next_older_member_age0のまま、next_older_member_nicknameは空の文字列のままになってしまっているのです。

if next_member[2] > compared_age and \
    next_older_member_age > next_member[2]:

そこで一行目の「next_older_member_agemembers[2]に入っている年齡より大きい」という条件を満たす最初のメンバーが現れた時に、next_older_member_ageがそのメンバーの年齡に必ず置き換わるように、next_older_member_ageの初期値にはどのメンバーの年齡よりも大きい値を代入しておく必要があります。

100歳までアイドルを続けているメンバーはどのグループにもさすがにいないので、next_older_member_ageの初期値には100を代入しておくことにします。

def next_older_member(members, nickname):
    compared_age = 0

    for next_member in members:
        if next_member[0] == nickname:
            compared_age = next_member[2]

    next_older_member_age = 100 # 初期値を変更
    next_older_member_nickname = ""

    for next_member in members:
        if next_member[2] > compared_age and next_older_member_age > next_member[2]:
            next_older_member_age = next_member[2]
            next_older_member_nickname = next_member[0]

    return next_older_member_nickname

これで無事、最後のメンバーまで比較を行うことができるようになりました😘完成です🌹💕

print(oldest_member([
    ("聡ちゃん", "松島聡", 19),
    ("マリ", "マリウス葉", 17),
    ("ケンティー", "中島健人", 23),
    ("風磨くん", "菊池風磨", 22),
    ("勝利くん", "佐藤勝利", 20)
], "風磨くん"))

Sexy Zoneのデータを使い、nickname"風磨くん"と指定してPythonで実行してみると…

❯ python3.6 next_older_member.py
ケンティー

さらにnickname"マリ"と指定してPythonで実行してみると…

❯ python3.6 next_older_member.py
聡ちゃん

これで我々は風磨くんとケンティーの「ふまけん💜💙」コンビ、マリと聡ちゃんの「聡マリ💚💛」コンビがこれから刻む新しいAgeを見逃さずに済みそうです。来たるSexy時代への大きな一歩を踏み出すことができました😉🥀

練習問題その5: 数年後のメンバーの年齢を正しく求める

いよいよ最後の問題です。またここにSexy Zoneのメンバーのデータを用意しました。一番右の行は3年後、東京オリンピックの年のメンバーの年齡です。ちょうど時代が追いついている頃です。

ニックネーム フルネーム 年齢 3年後
聡ちゃん 松島 聡 19 22
マリ マリウス 葉 17 20
ケンティー 中島 健人 23 26
風磨くん 菊池 風磨 22 25
勝利くん 佐藤 勝利 20 22

ここで一人、3年後の年齡が間違っているメンバーがいます。誰でしょう…🤔

ニックネーム フルネーム 年齢 3年後
聡ちゃん 松島 聡 19 22
マリ マリウス 葉 17 20
ケンティー 中島 健人 23 26
風磨くん 菊池 風磨 22 25
勝利くん 佐藤 勝利 20 ✗ 22
◯ 23

勝利くんの年齡が間違っています。新しいAgeを間違ってはいけません!

check_future_age関数

最後の問題では、check_future_ageという関数を実装してもらいます。この関数には、メンバーの現在の情報と、「何年後」かの情報を引数として指定します。そして「何年後」かの情報は、年齡が正しい場合と、間違っている場合があります。

こちらは3年後の情報を2つめの引数にしています。このように、正しい「何年後」かの情報を渡した場合、check_future_ageTrueを返します。

print(check_future_age(
    [
        ("聡ちゃん", "松島聡", 19),
        ("マリ", "マリウス葉", 17),
        ("ケンティー", "中島健人", 23),
        ("風磨くん", "菊池風磨", 22),
        ("勝利くん", "佐藤勝利", 20)
    ],
    # 正しい3年後の年齡
    [
        ("聡ちゃん", "松島聡", 22),
        ("マリ", "マリウス葉", 20),
        ("ケンティー", "中島健人", 26),
        ("風磨くん", "菊池風磨", 25),
        ("勝利くん", "佐藤勝利", 23)
    ]
))

# → Trueと出力

また、以下のように間違った情報を渡した場合、check_future_ageFalseを返します。

print(check_future_age(
    [
        ("聡ちゃん", "松島聡", 19),
        ("マリ", "マリウス葉", 17),
        ("ケンティー", "中島健人", 23),
        ("風磨くん", "菊池風磨", 22),
        ("勝利くん", "佐藤勝利", 20)
    ],
    # 間違った3年後の年齡
    [
        ("聡ちゃん", "松島聡", 22),
        ("マリ", "マリウス葉", 20),
        ("ケンティー", "中島健人", 26),
        ("風磨くん", "菊池風磨", 25),
        # ↓正しくは23歳
        ("勝利くん", "佐藤勝利", 22)
    ]
))

# → Falseと出力

メンバーの順番はひとつめの引数とふたつめの引数で変わりません。それでは、このcheck_future_age関数を書いてみましょう。

def check_future_age(current_members, future_members):
    # future_membersの年齡が、current_membersの年齡の
    # 「何年後」かの年齡を正しく表していたらTrueを返し、
    # そうでなければFalseを返す

Step1: 何を比較するかを考える

まずは、ひとつめの引数のリストとふたつめの引数のリスト、それぞれのリスト内の要素の[2]に入っている年齡を比べ、差を計算する必要があります。

この引数間の年齡の差が、すべてのメンバーについて同じであればTrueを、1人でも違えばFalseを返すようにすればよさそうです。

# 正しい例

# 聡ちゃんの年齢差は3
("聡ちゃん", "松島聡", 19)
("聡ちゃん", "松島聡", 22)

# マリの年齢差も3で、聡ちゃんと同じ
("マリ", "マリウス葉", 17)
("マリ", "マリウス葉", 21)
# 間違いの例

# 風磨くんの年齢差は3
("風磨くん", "菊池風磨", 22)
("風磨くん", "菊池風磨", 25)

# でも勝利君の年齢差は2 → 間違いなのでFalseを返す
("勝利くん", "佐藤勝利", 20)
("勝利くん", "佐藤勝利", 22)

そこで、forループを使って、順に

  1. 今調べているメンバーと、その次のメンバーのふたつの引数間の年齡の差をそれぞれ計算し
  2. その差を比較する

という処理を最後のメンバーまで繰り返し行うことにします。

def check_future_age(current_members, future_members):
    for # ???:
        # 今調べているメンバーと次のメンバーの
        # 引数間の年齡の差を比べる
        if # ???:
            # 一人でも違えば`False`を返す
            return False

    # 全員同じであれば`True`を返す
    return True

Step2: forループを書く

forループの中では、「今調べているメンバー」と、「その次のメンバー」の2人分の年齡が必要です。

そこでrange()を使い、変数iには、「今調べているメンバー」のリスト内の位置を順に渡せるようにします。

こうすることで、「その次のメンバー」の位置はi + 1で表せるようになります。

Sexy Zoneのようにメンバーが5人の場合、最後に比べるのは4人目のメンバー(i = 3)と5人目のメンバー(i + 1 = 4)の年齡です。

つまり、iには0から3、すなわちリストの要素の個数 - 2までの値を順に代入していけばよいことになります。

range(a, b)とすると、iにはaからb - 1の数字が入るので、range(0, len(current_members)-1)にすれば良さそうです。カウントずれに気をつけましょう!

def check_future_age(current_members, future_members):
    for i in range(0, len(current_members) - 1):
        if # ...

これで、調べたい2人のメンバーのリスト内の位置を表すことができるようになりました。

Step3: ifの中身を考える

ifの中身を考えていきます。

年齡はタプルの[2]に入っているので、今調べているメンバー、その次のメンバーのふたつの引数間の年齡の差は、それぞれ以下の式で表すことができます。

# 今調べているメンバーの年齡差
current_members[i][2] - future_members[i][2]

# その次のメンバーの年齡差
current_members[i+1][2] - future_members[i+1][2]

この2つの年齡差を比較し、同じでない場合はFalseを返したいので、ifの中には以下のような式を書くことにします。

def check_future_age(current_members, future_members):
    for i in range(0, len(current_members)-1):
        # 2つの年齢差が同じでない場合
        if (current_members[i][2] - future_members[i][2]) != \
           (current_members[i+1][2] - future_members[i+1][2]):
            # `False`を返す
            return False

    return True

完成です!🌹🌹

正しい例、間違った例でそれぞれ試してみます。

print(check_future_age(
    [
        ("聡ちゃん", "松島聡", 19),
        ("マリ", "マリウス葉", 17),
        ("ケンティー", "中島健人", 23),
        ("風磨くん", "菊池風磨", 22),
        ("勝利くん", "佐藤勝利", 20)
    ],
    # 正しい3年後の年齡
    [
        ("聡ちゃん", "松島聡", 22),
        ("マリ", "マリウス葉", 20),
        ("ケンティー", "中島健人", 26),
        ("風磨くん", "菊池風磨", 25),
        ("勝利くん", "佐藤勝利", 23)
    ]
))

print(check_future_age(
    [
        ("聡ちゃん", "松島聡", 19),
        ("マリ", "マリウス葉", 17),
        ("ケンティー", "中島健人", 23),
        ("風磨くん", "菊池風磨", 22),
        ("勝利くん", "佐藤勝利", 20)
    ],
    # 間違った3年後の年齡
    [
        ("聡ちゃん", "松島聡", 22),
        ("マリ", "マリウス葉", 20),
        ("ケンティー", "中島健人", 26),
        ("風磨くん", "菊池風磨", 25),
        # ↓正しくは23歳
        ("勝利くん", "佐藤勝利", 22)
    ]
))

ちゃんとTrueFalseと出力されました!

❯ python3.6 check_future_age.py
True
False

おわりに

以上、初心者向けのPython練習問題5問でした。少しでも世界平和とセクシー時代の構築に貢献できたら幸いです。ご感想はツイッターまでお寄せください。

最後に、ここまで読んでくださった皆さまはセクガル・セクボの素質があること間違いなしです!!!Sexy Zoneのリア恋枠担当、菊池風磨くん初主演ドラマ「吾輩の部屋である」月曜深夜24:59〜日本テレビ系にて放送中!!!!!!月曜深夜!!癒やしのほしい夜にぴったり!!!!!!(※もっと疲れるかもしれません。)

最後の最後に、来たる10月30日はなんと、Sexy Zoneの永遠の0番担当、佐藤勝利くんの21歳のBirthDayですおめでとうおめでとうおめでとう!!!!!タプルの内容を変えなければ…大人になられた…(感涙)

今日は以上です。ここまで読んでくださってSTY!!


Saya

東大卒アクセサリーデザイナー。詳しい自己紹介はこちら。Eメール: sayazamurai38@gmail.com