【python】ラムダ式・ラムダ関数を独習する

python

pythonのmin関数、max関数について調べる人

pythonのlambda式について調べる人
「pythonのラムダ式(lambda)について興味があります。どのように記述すればいいでしょう。ラムダ式はちょっと難しそうですが、ラムダ式と高階関数と言うのと何が違うのでしょうか?あと、コピペできるソースコード例も欲しいです。」

こんな疑問を解決します。

本記事のテーマ

この記事を書いている私はIT業界歴12年、年収1,000万円ちょっとの金融系エンジニアです。学生時代、修士論文のテーマはプログラミング言語研究だったため、ラムダ計算などを扱ってきました。

計算理論としてのラムダ計算やその実装であるラムダ式はいずれも計算機科学の分野では古典的な概念で、決して新しいものではありません。

当時はラムダ式を使うにはSMLやOcamlなどの関数型言語を使わなくてはなりませんでしたが、最近ではjavaをはじめとしたメジャーな言語でもlambda式を扱えるようになりました。その中でもpythonはとても扱いやすく生産性も高く、オススメの言語です。

本記事では、pythonのlambda式について解説します。lambda式は非常に強力で、多少複雑な処理も1行で記述できてしまいます。lambda式に興味のある方の参考になりますと幸いです。

1.ラムダ式とは

lambda式とは

lambda式の特徴を挙げます。

  • for文、while文を使わずに繰り返し処理ができる
  • 普通のプログラミングでは数行で書く処理を1行で書ける
  • バグが少なくコードが美しい

2.pythonでのラムダ式の構文

構文

lambda式の構文は以下の通り、とてもシンプルです。

lambda 《引数1》(, 《引数2》, …): 《処理》

簡単な例

ラムダ式の機能を理解するために、入れた数字に1を足した値(x+1)を返す簡単なlambda式を定義します。

>>> f=lambda x:x+1
>>> f(0)
1
>>> f(1)
2

例:lambda式で足し算(x+y)を定義する

>>> f=lambda x,y:x+y
>>> f(1,2)
3
>>> f(5,-6)
-1

3.関数を引数にとるラムダ関数を使う(map,filter)

関数を引数にとるラムダ関数を使う(map,filter)

lambda式を使う場合、for文は使わない

lispやOcamlなどの関数型言語には、lambda式のみでfor文やwhile文はありません。ざっくり言うとこれはキリスト教かイスラム教かみたいな話で、同じプログラミング言語でもforを使うかlambdaを使うかは、繰り返し処理に対する全く別の発想です。

pythonではlambda式もfor文・while文も両方扱えるのですが、lambda式内でfor文やwhile文は使いません。その代わり、map,filter,reduceなどを用いることで処理を行います。

簡単な例(map関数)

例:リストの各要素に1を足すlambda式を定義する。

>>> list(map(lambda x:x+1,[1,2,3,]))
[2, 3, 4]

同じ処理をfor文で書くと以下のようになります。

>>> [x +1 for x in [1, 2, 3]]
[2, 3, 4]

 

例:リストの各要素を2乗するlambda式を定義する。

>>> list(map(lambda x:x**2,[1,2,3]))
[1, 4, 9]

同じ処理をfor文で書くと以下のようになります。

>>> [x**2 for x in [1, 2, 3]]
[1, 4, 9]

少し複雑な処理(lambda式&filter関数)

for文では1行では書けないような複雑な処理もlambda式なら書くことができます。

例:階乗計算を行うプログラムと4の階乗計算(4*3*2*1)

>>> f = lambda x:1 if x<=1 else x*f(x-1)
>>> f(4)
24

 

例:filterで特定の要素のみを抽出する(

>>> list(filter(lambda x:x>=3,[1,2,3,4,5]))
[3, 4, 5]

4.関数を引数にとるラムダ関数を作る

関数を引数にとるラムダ関数を作る

lambda式を使うと、関数を引数に取る抽象的な関数を定義することができます。

例:値が変わらなくなるまで、繰り返し関数を適用し続ける
この例では、与えられた数字を2で割り続けます。0に収束して終了です。

>>> fixpoint=lambda f,x:x if x==f(x) else fixpoint(f,f(x))

>>> fixpoint(lambda x:round(x/2),10000)
0
>>> fixpoint(lambda x:round(x/2),-100)
0

 

例:同じ数字が二回連続出るまで、ランダムな数字を繰り返し生成し続ける

>>> import random
>>> fixpoint(lambda x:random.randint(1,x),100)
36

5.sum関数をラムダ式で定義する

sum関数をラムダ式で定義する

lambda式を使うと、sumやsort,addなどの基本的な関数を再帰を用いて1行で実装することができます。①基本ケースと②再帰処理を定義するだけ。

lambda式を使えば、次のようにsumを自分で定義できます。

  • 基本ケース:リストが空のとき、0を返す
  • 再帰処理:リスト末尾の値を取り出し、残ったリストを再帰処理した結果にその値を加える

具体的には次のようにコーディングします。定義する関数sは、sumと同じ動作の関数です。

>>> s=lambda l:0 if not l else l.pop()+s(l)
>>> s([1,1])
2
>>> sum([1,1])
2
>>> s([1,2,3,4,5,6,7,8,9,10])
55
>>> sum([1,2,3,4,5,6,7,8,9,10])
55

6.ラムダ関数:reduce関数を使う

reduce関数を使う

上述のように、自分でsum関数を再帰的に実装するのは少し頭を使います。しかしreduce関数を使うと、リストの各要素に対する畳み込み処理を用いて、より簡潔に記述することができます。

python3では、reduce関数はグローバル名前空間からfunctoolsというモジュールに移動してしまいました。

>>> from functools import reduce
>>> reduce(lambda a,b: a+b,[1,2,3,4,5,6,7,8,9,10])
55

 

reduce関数はどのような処理を行なっているのでしょう?

次の例で解説します。

>>> reduce(lambda xs,x: xs*10+x,[1,2,3,4,5,6,7,8,9])
123456789

これは、内部的に以下のように処理されます。

reduce(lambda xs,x: xs*10+x,[1,2,3,4,5,6,7,8,9])
=reduce(lambda [1,2,3,4,5,6,7,8]*10,9)
=reduce(lambda [10,20,30,40,50,60,70]*10,80)+9
=reduce(lambda [100,200,300,400,500,600]*10,700)+80+9
=reduce(lambda [1000,2000,3000,4000,5000]*10,600)+700+80+9
=reduce(lambda [10000,20000,30000,40000]*10,50000)+6000+700+80+9
=reduce(lambda [100000,200000,300000]*10,400000)+50000+6000+700+80+9
=reduce(lambda [1000000,2000000]*10,3000000)400000+50000+6000+700+80+9
=reduce(lambda [10000000]*10,20000000)+3000000+400000+50000+6000+700+80+9
=100000000+20000000+3000000+400000+50000+6000+700+80+9
=123456789

こちらの記事も参考になります

lambda式に興味があるのなら、こんなプログラミング言語もおすすめです

lambda式を本格的に勉強したいなら、関数型言語がおすすめです。

  • Ocaml ・・・実用的な拡張が施されているため、実践的なプログラムも可能
  • F# ・・・関数型言語をより実践的な言語に。マイクロソフトが開発
  • Haskell ・・・より美しくより論理的な言語仕様となっている

その他、lambda式が用いられる古典的な言語にListp,Scheme、その影響を受けた言語にSMLがあります。

今回は以上です。pythonでlambda式を使う際の参考にしていただきますと幸いです。