この記事は広告を含みます。
「関数型プログラミング」とは、プログラムを“関数”という部品を使って組み立てる考え方のようで、Rustの台頭で近年よく見かけるので、ちょっと調べながら、メモがてら記載していきます。
ふだんの Python プログラムでは、変数を作って値を書き換えたりしますが、関数型プログラミングの世界では
- できるだけ値を書き換えない(イミュータブル)
- 同じ入力ならいつでも同じ出力が返る関数(純粋関数)を使う
- 関数を「引数」として渡したり、関数から関数を返したりする(高階関数)
といったルールを大事にします。(もちろん、これだけじゃないよ)
例えば、、、 学校の数学を思い出すと。関数 f(x) は、ある数 𝑥 を入れたら決まった結果が返ってきます。たとえば、
f(x)=x×2 なら、𝑥 を 3 にすれば常に 6 が返ってくるし、8 を入れれば 16 が返ってくる。 何度やっても同じ答えですね。 これが「同じ入力なら同じ出力が返ってくる」という考え方のようです。
なので、Python でこの考え方をちょっと試してみてゆきます。
関数型風な書き方
map と filter を使う
map と filter は、「関数を引数に渡す」Python の機能の代表例のようです。
numbers = [1, 2, 3, 4, 5] # 1. map: すべての数を2倍にする double_numbers = list(map(lambda x: x * 2, numbers)) # 2. filter: 偶数だけにしぼりこむ even_numbers = list(filter(lambda x: x % 2 == 0, numbers)) print(double_numbers) # => [2, 4, 6, 8, 10] print(even_numbers) # => [2, 4]
どちらの操作も、もとの numbers
リストを書き換えずに、新しいリストを返しているところがポイントのようです。
これが「できるだけ値を書き換えずに、新しいものを作る」という関数型っぽい考え方のようです。
C++を頑張ってた時、値が変わると、入れていたアドレスが変わって、nullアクセスとかあったような。。。
同じ入力なら同じ結果を返す「純粋関数」
たとえば、次のような add_one
関数を考えます。
def add_one(x): return x + 1 print(add_one(3)) # => 4 print(add_one(3)) # => 4 (何度呼んでも必ず 3 に対して 4 を返す)
この関数は、同じ 3 を入れるといつでも 4 を返します。内部でほかの変数を変更するようなこともなく、単に「x + 1 した結果」を返しているだけです。 こうした関数が多いほど、「関数型プログラミング」に近い書き方になるようです。
一方、もし関数の中で外側の変数を書き換えたりしていると、 同じ入力でも結果が変わる場合があり、関数型の考え方から少し遠ざかってしまうようです。
まとめ
- 関数型プログラミングとは、「値をあまり書き換えず、同じ入力なら同じ結果を返す関数」をたくさん使ってプログラムを組み立てる方法。
- Python でも map や filter、lambda といった機能を使うと、関数型っぽい書き方ができる。
- 純粋関数を意識することで、バグを減らしたり、読みやすいコードを書いたりしやすくなる。
これだけでも、ふだんの Python とは少し違った考え方が味わえるので、慣れてからRustに挑戦するのも悪くない??
次回は、高階関数(Higher-Order Functions): 関数を引数として渡したり、関数から関数を返したりすることで、コードをより柔軟に組み立てる方法を学ぼうと思います。