1.11. Function(関数)¶
数学や物理でfunction(関数)といえば、$\sin$とか$\exp$のような初等関数をイメージすると思うが、情報処理の分野では、「ある入力に対して、何か出力してくれる手続きのまとまり」という意味でも使われる。この”function”という概念は、どのプログラミング言語でも極めて重要な概念である。どの言語でも基本的なfunctionは組み込まれているし(これをbuilt-in function、組み込み関数という)、module内で誰かが作ったfunctionを使うこともできる。同様に、自分で新たなfunctionを定義することもできる。なお、情報処理の分野では入力のことを一般に引数(ひきすう, argument)と呼び、出力のこと戻り値(return)と呼ぶ。
例として、$\sin ^2 x$を出力するfunction sin2
を作る。
import numpy as np
import matplotlib.pylab as plt
pythonでは、def 関数名(引数):
のように関数名を定義して、文末にコロン(:
)をつける。forループ、if文などと同じように、functionの中身の部分もインデントして書かなければいけない。
def sin2(x):
s = np.sin(x) #sはあとで使う
y = s**2
return y
まだ関数を作っただけで中身がない。定義域$~0 \leq x \lt 2 \pi~$の範囲で、$\sin x$と$\sin^2 x$をプロットして比較してみよう。
x = np.arange(0, 2*np.pi, 0.1)
plt.plot(x, np.sin(x))
plt.plot(x, sin2(x));
$\sin^2x$は周波数が2倍で正の値のみを取ることが分かる。
名前空間、スコープ、隠ぺい、カプセル化¶
functionを使うとき注意したい点がいくつかある。まず、functionはinputからoutputを出力する装置なので、funtion内で使われている変数、たとえば、2行目のs
はfunctionの外で読み出すことができない。したがって、
print(s)
とすると、s
は定義されていないとpythonに怒られる。なぜこのような仕様になっているかというと、functionというものを、inputを入れたらoutputを返す単なるブラックボックスとして扱いたいからで、function中で使われている変数は外部に漏らしたくないからだ。このことをカプセル化や隠ぺいと呼んだりする。もしfunction内で使われている変数がfunctionの外で無制限に読み出せたとすると、他の部分で使われている変数名とfunction内で使われている変数がたまたま同じ名前だった場合、上書きされてしまう。だから上書きされないように、function内で使われている変数(これをローカル変数という)はreturn
で返す以外は外に出られない仕様になっている。「じゃあ変数名が被らないように注意して名前を付ければいいじゃん」と思うかもしれないが、コード内の変数がすべて被らないようにしようとすると、たとえば巨大なコードを大人数で作ろうとした場合、プログラマー全員がすべての変数名のリストを持っていて、名前が被っていないかどうかいちいちチェックしないといけなくなり、作業量が膨大になる。一方、functionをカプセル化して変数を読み出せないようにしておけば、大人数で大きなコードを書く場合に、functionの中でどんな変数名が使われていようと名前が被る心配はない。そのfunctionを作る担当者以外はfunctionの中身を知らなくても問題ないので、作業効率が格段に上がる。また、変数の値を取り出せない仕様にしているので、functionを使い終わったら、物理メモリから値を消去することができ、メモリの消費を抑える効果もある。なお、コード全体で使いたい変数はグローバル変数と呼び、変数の有効範囲のことを名前空間(namespace)やスコープという。
下の例では、メインの部分で定義されている変数a
をchange
というfunctionで書き換えようとして、function内では書き換わっているものの、メインの部分では書き換わっていないことを示している。
a = 2 #グローバル変数
def change():
a = 3 #ローカル変数
print("a in function is" , a)
change()
print("a in main is" , a)
ここで一つ注意したいことがある。上で定義したchange
というfunctionには引数がない。そこで、change()
ではなくchange
としてしまう間違いが入門者に非常に多い。正しい書き方であるchange()
とすると、
change()
と正しく表示されるが、()
を書き忘れて、change
とすると、
change
となってなんだか変な結果が返ってくる(この文の意味はclassと属性の構造が分かると理解できるようになる)。引数のないfunctionは、引数がないことを明示するために()
を省略してはいけないのだが、そのことを忘れるとコードを書くときハマってしまうので気を付けてほしい。imshow
でカラーバーを表示させたいとき、colorbar()
のように()
を付けなければいけなかったのは、colorbar
が引数を書かなくてもよいfunctionであるためで、()
を省略してはいけないからだ。
練習問題¶
(1.11.1.) 練習問題(1.9.2)で紹介したフィボナッチ数列を考える。桁数を入力したら、その桁数のフィボナッチ数列をすべて生成するfunction fibonacci
を作れ。また、このfibonacci
を使って、8桁までのフィボナッチ数列を求めよ。
(1.11.2.) 前問のfunction fibonacci
を使って、ある整数を入力したとき、その整数に最も近いフィボナッチ数を与えるfunction find_fibonacci
を作れ。
解答例¶
(1.11.1.) (1.9.2.)の解答をほとんどそのままfunctionに書き直す。ただし、桁数をn
としてfunction fibonacci
の引数にする。
import numpy as np
def fibonacci(n):
F = np.array([0, 1])
i = 1
Fi = np.array([1])
while np.log10(Fi) < n:
F = np.concatenate([F, Fi])
i += 1
Fi = np.array([F[i-1] + F[i]])
return F
8桁までなので、引数に8と書けば答えがでる。
fibonacci(8)
確認のため桁も調べておく。
print(np.log10(np.max(fibonacci(8))))
(1.11.2.) function fibonacci
は別のfunctionの内部でも使用することができるので、find_fibonacci
内でもこれを利用する。
def find_fibonacci(x):
d = np.ceil(np.log10(x))
F = fibonacci(d)
index = np.abs(F-x).argmin()
return F[index]
比較したいある整数をx
として引数にする。fibonacci
は桁数を引数にとっているので、x
の桁数を求め、np.ceil
を使った切り上げにより、それより一桁大きいすべてのフィボナッチ数列F
を求める。次にF
の要素のうち、x
に最も近い要素を差の絶対値が最小になることを利用して、np.abs
と.argmin
を使って求めるという流れ。.argmin
はその配列の中で最小の値を持つ要素の要素番号を与える属性である。例として、あるい整数x
は適当に$22333$としてみた。
x = 22333
a = find_fibonacci(x)
print(a)
なお確認だが、find_fibonacci
ではフィボナッチ数列F
そのものではなく一番近いフィボナッチ数F[index]
をreturnしているので、F
自体は以下のように隠ぺいされていて、取り出すことはできない。
print(F)
当サイトのテキスト・画像の無断転載・複製を固く禁じます。
Unauthorized copying and replication of the contents of this site, text and images are strictly prohibited.
© 2019 Go Yusa