関数型プログラミング

 

 

関数型プログラミング

 Python は オブジェクト指向言語 でありながら、関数型プログラミング としての機能も備えています。命令を処理するために複数の手法(パラダイム)を備える言語のことをマルチパラダイム言語とよぶことがありますが、Python もそうしたマルチパラダイム言語のひとつです。

 オブジェクト指向において、オブジェクトは内部データとそれを変更するためのメソッドが一体となったカプセルであり、プログラムはメソッドの使用によって内部データを変化させていく手続きです。一方で関数型プログラミングにおいては、できる限り状態の変更を避けるようにして、プログラムは関数同士で受け渡されるデータの流れ(複数の関数の組み合わせ)として表現されます。
 
 Python においては、イテレータ、ジェネレータ、高階関数、デコレータなどの機能を使って関数型スタイルのコードを記述します。こうしたツールを使いこなせるようになると、驚くほど短く簡潔なコードを書くことができるようになります。

 とはいえ、先にも述べたように、Python は純関数型言語ではないので、完全に首尾一貫した関数型プログラミングのコードを記述は困難ですし、またその方向を目指すことは現実的ではないでしょう。普通は他の方法と組合わせたほうがシンプルで分かりやすいコードになるはずです。だから敢えて関数型プログラミングではなく、関数型スタイル と表現しています。関数型プログラミングの概念は非常に奥深いものですが、Python を扱う限りにおいては「関数型プログラミングとは何ぞや?」ということを突き詰める必要はありません。まずは上のメニューに並んでいるリンク先の記事を読み、サンプルコードの動作を確認しながら、関数型プログラミングの雰囲気を掴んでみてください。状況に応じて「この部分は関数型スタイルで書いたほうが効率的だ」のように判断できるようになれば、それで十分だし実践的だと思います。

 

関数オブジェクト

 Python で関数を使用するときは、関数名(引数) と記述します。コードを実行すると、その関数の機能に応じて様々なクラスのオブジェクトが返ります。たとえば、オブジェクトの長さを返す組み込み関数 len() に "Python" という文字列を渡すと「6」という intクラスのオブジェクトが返ります。

# FUNC_01

# In[1]

x = len("Python")

print(x)
print(type(x))
6
<class 'int'>

 今度は () を添えずに len という関数名だけを変数 x に渡して、print(x) で表示させてみます。

# In[2]

x = len

print(x)
print(type(x))
<built-in function len>
<class 'builtin_function_or_method'>

 変数 x は関数オブジェクト (builtin_function_or_method クラスのインスタンス)を参照していることがわかります。x() にオブジェクトを渡すと、そのオブジェクトは参照先の len() に渡され、オブジェクトの長さを返します。

# In[3]

# xに関数lenを格納
x = len

# 関数xに文字列を渡す
y = x("Python")

print(y)
6

 Python ではすべてがオブジェクトです。もちろん関数もオブジェクトです。
 関数をオブジェクトであることを明示するために、関数のことを敢えて 関数オブジェクト とよぶことがあります。関数型プログラミングでは関数オブジェクトの受け渡しがコードの核となります。

 Python のリストはほとんどあらゆるオブジェクトを格納できます。もちろん、関数オブジェクトを要素にもつこともできます。それほど頻繁に使うわけではありませんが、複数の関数をリストにまとめておくと便利なこともあります。まずは簡単な例を見てみましょう。

# FUNC_02

# In[1]

import math

# 関数リスト
func_list = [math.sqrt, math.exp, math.cos]

# √10, exp(10), cos(10)を計算
for f in func_list:
    print(f(10.0))
3.1622776601683795
22026.465794806718
-0.8390715290764524

 このコードのループ処理では最初にループ変数 f に関数オブジェクト math.sqrt を入れてます。そして関数 f に 10.0 を渡して平方根を表示します。次に f に math.exp を入れて、10.0 を渡してexp(10.0) を表示します。最後に f に math.cos を入れて、10.0 を渡して cos(10.0) を表示させています。

 次のサンプルは、あるオブジェクトを float() に渡し、その戻り値を int() に渡し、その戻り値を str() に渡すというコードです。

# In[2]

# 関数のリスト
func_list = [float, int, str]

x = "10.5"

for k in range(3):
    x = func_list[k](x)

print(x)
10

 最終的に「10」という文字列 (strクラスのオブジェクト) が返ります。ループ処理のブロックに記述されている

x = func_list[k](x)

という部分では、func_list のインデックス k の要素に x を渡し、その戻り値を x に入れるという再帰的な処理が行われています。