空飛ぶチンアナゴの統計解析日記

統計解析を嗜むチンアナゴのメモ帳です

本当に初心者が学んでほしいコードの話 〜その3 関数の話3〜

さて、いよいよここからは自分で関数を書きます。
各言語によってお作法は若干異なりますが、基本的なところは大体同じです。
今回はPythonをベースに記載を進めていきたいと思います。

振り返り 〜関数とは〜

f:id:flying-spotted-garden-eel:20210823111036p:plain
なんかよくわからん箱に、何かを突っ込むと、何かが返ってくる。
それだけ思い出していただければまずは大丈夫です。

関数の定義の仕方

def 関数の名前(突っ込むやつ):
    以下中身

ざっくりとdefのあとに1つ「半角」スペースを空けて関数の名前を書く。
そしてそのあとに「半角の」()を書いて、「半角」の:を書く。
もし、突っ込むものを定義する必要があれば半角括弧の中に記入しましょう。
これで関数の宣言は完了です。
中身は:を改行してから書きます。行頭は半角スペース4文字入れてから中身を書くようにしましょう。また中身が複数行に渡る場合は中身の行頭の位置を揃えるようにしましょう。
例えば、

def print_hoge():
    print('hogehoge')
    print('mogemoge')

は許容されますが、

def print_hoge():
    print('hogehoge')
       print('mogemoge')

は許容されません。おそらく「SyntaxError: unexpected indent」としてエラーが出るはずです。

今日の課題:平均値を求める関数を書く(ただし、他の関数は使わずに)

以上を踏まえた上で関数を実際に書きながら設明しましょう。
関数名は「mean_list*1」として、リストを整数が幾つか並んだリストを突っ込むと平均値を計算してくれる関数です。
今回は組み込み関数であるsum()とlen()はあえて使わずに書きます。

1)まず、関数の挙動をどうするか?

いうまでもなく関数がどのような仕事をするのか事前にある程度考えておかなければいけません。
「ある程度」が付いているのは実際にコードを書いていく中で、方法を大きく修正する必要がある場合があるからです。
今回やるべきことは大体こんな感じでしょうか?

1. 平均を求めたいリストを求める
2. リストの中身の合計値を求める
3. リストの中身の個数を求める
4. 2.を3.で除する
5. 4.で求めた平均値を返す

この流れに沿ってコードを書いていきたいと思います。

2)コードを書こう

とりあえず関数名を定義する
def mean_list():
     pass

最初の関数の定義の仕方に則って関数を定義するとこのような感じになります。
passは「関数の中に何も入れないと怒られるので、仕方なく入れる単語」として認識していても大丈夫です*2。最終的にこのpassはいなくなります。
これで関数mean_listは定義できましたが、何もない「ただの空き箱」ですのでこの関数を実行したところで何も起きません。

リストを突っ込み合計値と長さを求める

mean_list()にリストを突っ込むのでmean_list()の引数を定義しましょう。
引数の名前はぶっちゃけなんでもいいのですが、関数が複雑になると訳がわからなくなるのでその引数がなんなのかわかるように名前をつけておくのが賢明です。
今回は引数をlist*3として、書き始める。

def mean_list(list):
     pass

さて、ここから合計値を求める。
合計値を求めるには、「リストの中のものを一つ取り出して、それをそれまでの和に合計する」ことで求められる。一番簡単なものはforループを使う方法なので、forループを使って合計値を求める。

def mean_list(list):
    # リストの和を置く場所を作る
    list_sum = 0
    for value in list:
        list_sum = list_sum + value
    
    #リストの合計値を表示するために便宜的においている(あとで消す)
    print(list_sum)

さて、この関数を定義して適当な全て数値型のデータが入ったリストを入れてあげると、リストの合計値が表示されるはずである。
次にリストの長さを求める訳だが、「リストの長さ == ループを繰り返した回数」である。ということはループをした回数を数えるカウンターを実装してあげれば良い。
したがってカウンターは下記のコードとして追加することができる。

def mean_list(list):
    # リストの和を置く場所を作る
    list_sum = 0

    # リストの長さを置く場所を作る
    list_length = 0

    for value in list:
        list_sum = list_sum + value
        list_length = list_length + 1
    
    #リストの合計値と長さを表示するために便宜的においている(あとで消す)
    print(list_sum)
    print(list_length)
平均値を求める

さて、リストの合計値とリストの長さがわかれば、合計値をリストの長さで除してこのプログラムは終わりである。
早速追加しよう。

def mean_list(list):
    # リストの和を置く場所を作る
    list_sum = 0

    # リストの長さを置く場所を作る
    list_length = 0

    for value in list:
        list_sum = list_sum + value
        list_length = list_length + 1

    list_mean = list_sum / list_length
    
    #リストの合計値、長さ、平均値を表示するために便宜的においている(あとで消す)
    print(list_sum)
    print(list_length)
    print(list_mean)

これで終わりである。
さて、無事に「リストの合計値」、「リストの長さ」、「リストの平均値」がそれぞれ表示されたはずである。
あとは、print(list_sum)とprint(list_length)を関数から消せば「リストの平均値を表示する」関数の出来上がりである。

3)関数の求めた値を他でも使いたい。

さて、

def mean_list(list):
    # リストの和を置く場所を作る
    list_sum = 0

    # リストの長さを置く場所を作る
    list_length = 0

    for value in list:
        list_sum = list_sum + value
        list_length = list_length + 1

    list_mean = list_sum / list_length
    
    #リストの平均値を表示する
    print(list_mean)

list = [1, 9, 1, 9, 4, 5, 4, 5]
list_average = mean_list(list)

というコードを書いて実行した場合、list_averageに関数で求めた平均値は代入されているか?
実際にやってみるとわかるが、「何も代入されていない」
これは関数の中にある、list_sum、list_length、list_meanの3つの変数は関数の中でのみ存在する変数であり、関数の外に持ち出すには持ち出すための手続きが必要となる。このように特定の領域の中のみで有効な変数を専門用語でlocal variable(日本語だと局所変数)と呼ぶ。
では持ち出すための手続きのコードを追加しよう。

def mean_list(list):
    # リストの和を置く場所を作る
    list_sum = 0

    # リストの長さを置く場所を作る
    list_length = 0

    for value in list:
        list_sum = list_sum + value
        list_length = list_length + 1

    list_mean = list_sum / list_length
    
    #リストの平均値を表示する
    print(list_mean)
    return list_mean

list = [1, 9, 1, 9, 4, 5, 4, 5]
list_average = mean_list(list)

これでmean_listで算出した平均値をlist_averageに格納できるようになった。
追加したのは「return list_mean」のみである。
returnで関数の戻り値を定義する文である。今回は「return list_mean」として、戻り値をlist_meanに定義してあげることで、関数の外にlist_meanを持ち出せるようになった。

まとめ

関数の定義についての流れを今回は説明した。
もっと複雑な関数を定義するときもこれと同様の手順で定義すれば問題なく定義できる。

おまけ:追加の課題

1)任意の整数が入ったリストについて「中央値」を求めてください。
2)任意の整数が入ったリストについて「最頻値」を求めてください。

最頻値は少し難しいかもしれませんがぜひ挑戦してみてください。

*1:組み込み関数と名前がカブるのを避ける為あえてこういう名前にしている

*2:https://docs.python.org/ja/3/tutorial/controlflow.html#pass-statements

*3:今回はlistは1つのみなので単にlistとしてもいいが、実際はinput_listのように少し引数名にも説明を加えたほうがいい