[Python]プログラミング初心者に関数から教えてみたことを振り返る

Pythonに限らず、プログラム入門というと、環境構築、Hello World、四則演算の次に、
1. if...elseによる条件分岐
2. for, whileの繰り返し処理
という流れで、その後に
3. 関数
が出てくることが多いと思う。

しかし、個人的には「変更に強い業務プログラムを作る」ためには、初心者にこの順番で教えるのがベストなのだろうか？とずっと思っていた。

そこで、ある小さなチームにpythonプログラミングの手ほどきする機会を得た際に、一般的な教科書の手順ではない「関数」から教えてみた。そして、if文で「else」を使わない思想を最初に仕込んでみた。

その対応の経緯を振り返り、気づいたことなどをまとめる。

以下、心得のある方にとっては当たり前の記述ばかりで、目新しいテクニックやトピックスはない。また、環境構築、Hello World、四則演算あたりまではできて、その後どうしたのかをまとめる。

#背景
メンバにはプログラミング初心者がひとりいたが、「R」などを使って自分の研究用のプログラムを書いているメンバもいた。

チームが最終的に目指すしているのは「最終的に業務システムを作れるようになりたい」だったので、手ほどきの方針として、次の3点を重視することを、最初に説明している。

- バグを減らすためには、分岐の数を減らすのが第一という思想でカリキュラムを組み立てる。
- 変更が必ず発生する前提で、変更に強いプログラムとは何かを一緒に考える。
- 他のメンバが作ったプログラムや、自分が数か月前に作ったプログラムの「読みやすさ」を考える。

#環境
- Python 3.7.4

#お題
チームが目指しているのは、とある業務に使うプログラムだったので、手ほどきの際のお題は業務テーマに沿って作成した。

- 料金表に従い、人数に応じた一人当たり料金を計算する。

|人数 |料金 |
|:---:|:---:|
|10以下 |980 |
|11～20 |840 |
|21以上 |720 |

#最初に教えたことは、関数
目的の料金を得るために、インプットとして何が必要かを考えてもらった。そして、目的の値を「戻り値」、インプットを「引数」とする「関数」の書き方を教えた。

```Python:fee_calculate.py
#スケルトン
def fee_calculate(num): #人数から料金を求める関数
    #do_something
    fee = 980 #とりあえず動くように
    return(fee)

num = 5 #人数を設定する
fee = fee_calculate(num) #関数を使って料金を求める
print(fee) #結果を表示する「980」
```
関数の中身は何もないのだが「欲しい値は、関数を使って得る」ということを刷り込んだ。

#次に教えたことは、elseを使わないこと
次に、人数に応じた料金の判定の仕方を教えた。
加えて、「if文のさまざまな書き方は読めた方がよい。しかし、自ら積極的に使う必要はない。」という考え方を紹介した。

```Python:fee_calculate.py
#料金算定
def fee_calculate(num): #人数から料金を求める関数
    if 1 <= num <= 10:
        fee = 980
        return(fee) #結果が出たらすぐにリターン

    if 11 <= num <= 19:
        fee = 840
        return(fee) #結果が出たらすぐにリターン

    if 20 <= num:
        fee = 720
        return(fee)

num = 5 #人数を設定する
fee = fee_calculate(num) #関数を使って料金を求める
print(fee) #結果を表示する「980」
```

料金を求めたら、さっさと`return`する。`else`や`elif`を使うアンチパターンと比較して、書きやすさ、読みやすさ、変更しやすさをメンバと議論した。

```Python
#アンチパターン
def fee_calculate_a(num):
    if 1 <= num <= 10:
        fee = 980
    elif 11 <= num <= 19: #elifを使ってみる
        fee = 840
    else: #elseも使ってみる
        fee = 720
    return(fee) #結果をリターン

num = 21 #人数を設定する
fee = fee_calculate_a(num) #関数を使って料金を求める
print(fee) #結果を表示する「720」
```

これくらい小さなプログラムだと、書きやすさ、読みやすさ、変更しやすさの違いは実感できないかも・・・と思っていたが、プログラム経験のあるメンバが、いい感じにこの考え方に賛同してくれた。結果的に、参加者が意見を出し合って考える場になったので助かった。チームの意見は民主的に決まっていくものなので、初心者メンバは「こういうものなんだ」と思ってくれたみたいだ。

#関数から教えてみたことからの気づき
「欲しい値は、関数を使って得る」という考え方を身につけて関数の呼び方を覚えておくことで、ライブラリを使ってみることへの手ほどきが、早い段階からできたのではないかと感じている。(例えば`calender`を使って月の日数を求めてみるとか。)

自分でアルゴリズムを考えて作ってみることも大事だが、世の中にたくさんある「便利なライブラリ」をどう見つけて使っていくのか、それをどう組み合わせていくのか。今、プログラム言語を学ぶならそういうことを身につけることも大切だと思う。

#この後どうなったのか？
数回に分けて勉強会という形で、Pythonの手ほどきをさせてもらったが、初心者メンバは順調に成長していると思う。つまり、「やりたい事を考えて、まずはググってライブラリを探す。」という行動が生まれているから。関数の使い方が理解できれば、後は`import`を覚えれば使えるライブラリがたくさんある。

勉強会は、お題を出して、どう解決するかを考え、プログラムを作ってみるという練習をしながら、業務システムを作るうええで欠かせない「テスト」や「エラー処理」や「ログ出力」の考え方や対応方法を一緒に学んでいる。

Python3は「テスト」ツールや「エラー処理」や「ログ出力」のライブラリが充実しているが、どういう考え方で作られていて、どう使えばベターなのか、早い段階で学び、考えることが重要となるからだ。

#繰り返しはいつ教えたのか
結論を述べると、はっきりと教えないうちにみんなできていた。学び方を覚えてしまえば、教えるよりも自分で学ぶ方が早いということだ。

ちなみに、先ほどの料金算定のプログラムなら、複数の申し込み人数を一括で処理したいときに繰り返しを使う。

申し込み人数のリストに対して繰り返し料金算定するサンプルは次のようになる。

```Python
#繰り返し
list = [5, 11, 21, 7, 15, 30] #申し込み人数のリスト

def fee_calculate(num): #人数から料金を求める関数
    if 1 <= num <= 10:
        fee = 980
        return(fee)

    if 11 <= num <= 19:
        fee = 840
        return(fee)

    if 20 <= num:
        fee = 720
        return(fee)

for num in list: #リストの中身を1件ずつ取り出す
    fee = fee_calculate(num) #関数を使って料金を求める
    print(fee) #結果を表示する
```

#先に繰り返しを学んだ時に起こりがちなこと
まずループを回す発想になり、後で関数を切り出すことができなくなる。(この手順で後から関数化するのは面倒以外の何物でもないので。)
個人的にちゃちゃっと使い捨てのプログラムを作るとき以外はこういう作り方ばかりしていると、プログラミングスキルが向上していきにくいんじゃないかと思っている。

```Python
#アンチパターン
list = [5, 11, 21, 7, 15, 30] #申し込み人数のリスト

for num in list: #リストの中身を1件ずつ取り出す
    if 0 <= num <= 10: #取り出した人数を判定して・・・
        fee = 980
    elif 11 <= num <= 19:
        fee = 840
    else:
        fee = 720
    print(fee)
```

2つのプログラムを比較すると、短くいのはアンチパターンだが、読みやすいのはどちらか、そういうことを考えるきっかけになったら嬉しい。

なお、個人的には`else`と`elif`は書くのは簡単だが、読むときは前の条件＜も＞見なければいけないのがしんどく感じる。日本語でも「このときはこう。」と言ってくれた方が分かりやすい。「それ以外のこういうときは、」と言われると「それってどれ？」となるから。なので、必然性のない`else`と`elif`は使わないように心掛けている。

#参考
[分岐アンチパターン](https://qiita.com/pakkun/items/9bef9132f168ba0befd7)