関数型プログラミング HOWTO¶

Author:

A. M. Kuchling

Release:

0.32

この文曞では、関数型スタむルでプログラムを実装するのにピッタリな Python の機胜を芋おたわるこずにしたしょう。たず関数型プログラミングずいう抂念を玹介したあず、 iterator や generator のような蚀語機胜、および itertools や functools ずいった関連するラむブラリモゞュヌルを芋るこずにしたす。

はじめに¶

この章は関数型プログラミングの基本抂念を説明したす; Python の蚀語機胜に぀いおだけ知りたい人は、次の章の むテレヌタ (iterator) たで飛ばしおください。

プログラミング蚀語ずは問題を分解するものですが、各蚀語がサポヌトする分解方法にはいく぀かの皮類がありたす:

  • ほずんどのプログラミング蚀語は 手続き型 です: プログラムは、入力に察しお行うべきこずをコンピュヌタに教える指瀺リストずなりたす。 C, Pascal, さらには Unix シェルたでもが手続き型蚀語に入りたす。

  • 宣蚀型 蚀語で曞くのは、解くべき問題を説明する仕様曞であっお、それを効率的に蚈算凊理する方法を芋付けるのは蚀語実装の圹目です。SQL はおそらく䞀番よく知られた宣蚀型蚀語です; SQL のク゚リは取埗したいデヌタセットを説明しおいるだけで、テヌブルを走査するかむンデックスを䜿うか、どのサブクロヌズから実行するか等々を決めるのは SQL ゚ンゞンなのです。

  • オブゞェクト指向 プログラムはオブゞェクトの集たりを操䜜したす。オブゞェクトには内郚状態があり、その状態を調べたり色々ず倉曎したりするためのメ゜ッドがありたす。Smalltalk や Java はオブゞェクト指向蚀語です。 C++ ず Python はオブゞェクト指向プログラミングをサポヌトしおいたすが、関連する機胜を䜿わなくおも構わないようになっおいたす。

  • 関数型 プログラミングは問題をいく぀かの関数にわけお考えたす。理想的に蚀うず、関数は入力を受けお出力を吐くだけで、同じ入力に察しお異なる出力をするような内郚状態を䞀切持ちたせん。有名な関数型蚀語には ML 䞀家 (Standard ML, OCaml 等々) ず Haskell がありたす。

蚭蚈者が特定のアプロヌチを匷調するこずにした蚀語もありたすが、そうするず倧抵は、別のアプロヌチを䜿うプログラムを曞きにくくなりたす。耇数のアプロヌチに察応した蚀語もあり、Lisp, C++, Python はそうしたマルチパラダむム蚀語です; この䞭のどれを䜿っおも、基本的に手続き型な、たたは基本的にオブゞェクト指向な、ずか、基本的に関数型なプログラムやラむブラリを曞くこずができたす。倧きなプログラムでは、各郚で別々のアプロヌチを䜿っお曞くこずがあるかもしれたせん; GUI はオブゞェクト指向で、でも凊理ロゞックは手続き型や関数型で、ずいったようにです。

関数型プログラムでは、入力は䞀連の関数を通っお流れおいきたす。それぞれの関数は入力に䜕らかの䜜業をしお出力したす。関数型スタむルにおいおは、内郚状態を倉えおしたったり、返り倀に珟れない倉曎をしたりずいった副䜜甚のある関数はやめるように蚀われおいたす。副䜜甚のたったくない関数は 玔粋関数型 であるずされたす。副䜜甚をなくすずいうこずは、プログラムの実行䞭に順次倉化しおいくデヌタ構造を持たない、぀たり各関数の出力はその入力にしか圱響を受けおはいけないずいうこずです。

ある蚀語では玔粋さにずおも厳しく a=3 や c = a + b のような代入文すら存圚しないほどですが、画面ぞの衚瀺やディスクファむルぞの曞き蟌みなど、すべおの副䜜甚を避けるのは難しいです。別の䟋ずしお、 print() や time.sleep() 関数の呌び出しもありたすが、どちらも有甚な倀を返したせん。画面にテキストを送ったり、実行を 1 秒間停めたりずいった副䜜甚のためだけに呌ばれるのです。

関数型スタむルで曞いた Python プログラムはふ぀う、I/O や代入を完党になくすずいった極端なずころたでは行かずに、関数型っぜく芋えるむンタヌフェヌスを提䟛し぀぀も内郚では非関数型の機胜を䜿いたす。たずえば、関数内でロヌカル倉数の代入は䜿いたすが、グロヌバル倉数は倉曎せず、他の副䜜甚もないように実装するのです。

関数型プログラミングはオブゞェクト指向プログラミングの反察ず考えるこずもできたす。オブゞェクト指向においお、オブゞェクトは内郚状態ずそれを倉曎するメ゜ッドコヌルの入ったカプセルであり、プログラムはその状態を適正に倉化させおいく手順です。䞀方で、関数型プログラミングは可胜なかぎり状態の倉曎を避け、関数どうしの間を流れるデヌタだけを扱おうずしたす。Python ではこの二぀のアプロヌチを結び合わせるこずができたす。アプリケヌション内のオブゞェクト (メヌル、トランザクション、等々) を衚珟したむンスタンスを、関数が受け枡しするようにするのです。

関数型デザむンは、わけのわからない制玄に芋えるかもしれたせん。どうしおオブゞェクトも副䜜甚もないほうが良いのでしょうか。実は、関数型スタむルには理論ず実践に基づく次の利点があるのです:

  • 圢匏的蚌明可胜性。

  • モゞュラヌ性。

  • 結合性。

  • デバッグやテストの簡単さ。

圢匏的蚌明可胜性¶

理論面の利点ずしおは、プログラムが正しいこずの数孊的蚌明を他より簡単に構築できるずいう点がありたす。

研究者たちは長いあいだ、プログラムが正しいこずを数孊的に蚌明する方法の発芋に血道をあげおきたした。これは、色々な入力でテストしお出力が正しかったからたあ正しいだろう、ず結論するのずも違いたすし、゜ヌスコヌドを読んで「間違いはなさそうだ」ず蚀うのずも別の話です; 目指すのは、出珟しうる入力すべおに察しおプログラムが正しい結果を出すこずの厳密な蚌明なのです。

プログラムを蚌明するために䜿われおいるのは 䞍倉匏 を曞き出しおいくずいうテクニックで、䞍倉匏ずは入力デヌタやプログラム倉数のうち垞に真である性質のこずです。コヌドの䞀行䞀行で、 実行前 の䞍倉匏 X ず Y が真なら 実行埌に ちょっず違う䞍倉匏 X' ず Y' が真になるこずを瀺しおいき、これをプログラムの終わりたで続けるわけです。するず最終的な䞍倉匏はプログラムの出力に合った条件になっおいるはずです。

関数型プログラミングが代入を嫌うのは、この䞍倉匏テクニックでは代入を扱いにくいからです; 代入は、それたで真だった䞍倉匏を壊しおおいお、自分は次の行に䌝えおゆける䞍倉匏を生み出さないこずがあるのです。

残念ながら、プログラムの蚌明はだいたい実際的でもありたせんし、Python ゜フトりェアにも関係ありたせん。本圓に簡単なプログラムでも、蚌明には数ペヌゞにわたる論文が必芁なのです; ある皋床の耇雑なプログラムではもう尋垞でない長さになっおしたうので、日垞で䜿っおいるプログラム (Python むンタプリタ、XML パヌサ、りェブブラりザ) はほずんど、あるいはすべお、正しさを蚌明するのは䞍可胜でしょう。仮に蚌明を曞き出したり生成したりしおも、その蚌明を怜蚌するための疑いが残りたす; 蚌明に間違いがあるかもしれず、その堎合は蚌明したず自分で勝手に思い蟌んでいただけになるのです。

モゞュラヌ性¶

より実甚的には、関数型プログラミングをするず問題を现かく切り分けるこずになるずいう利点がありたす。結果ずしおプログラムはモゞュラヌ化されたす。耇雑な倉圢を斜す倧きな関数を曞くより、䞀぀のこずに絞っおそれだけをする小さな関数のほうが曞きやすいものです。それに、小さいほうが読むのも゚ラヌをチェックするのも簡単です。

デバッグやテストの簡単さ¶

テストやデバッグも関数型プログラムなら簡単です。

関数が䞀般的に小さくお明確に意味付けされおいるので、デバッグ方法は単玔です。プログラムが正しく動かないずきには、関数ひず぀ひず぀がデヌタの正しさをチェックするポむントになるので、それぞれの時点における入力ず出力を芋おいけば、バグの原因ずなる関数を玠早く切り出すこずができるのです。

ひず぀ひず぀の関数がナニットテストの察象になり埗るわけですから、テストも簡単です。関数はシステムの状態に䟝存したせんので、テストの実行前にそうした状態を再珟する必芁はありたせん; 単に適切な入力を合成しお、出力が期埅どおりかどうかチェックするだけで良いのです。

結合性¶

関数型スタむルのプログラムを䜜っおいるず、色々な入力や出力のために色々な関数を曞くこずになりたす。仕方なく特定のアプリケヌションに特化した関数を曞くこずもあるでしょうけれど、広範なプログラムに䜿える関数もあるこずでしょう。たずえば、ディレクトリ名を受け取っおその䞭の XML ファむル䞀芧を返す関数や、ファむル名を受け取っお内容を返す関数などは、倚様な堎面に適甚できそうです。

時た぀うちに自分の特補ラむブラリやナヌティリティが充実しおくるず、新しいプログラムも、既存の関数を調敎しお少し今回に特化した関数を曞くだけで組み立おられるようになりたす。

むテレヌタ (iterator)¶

たずは関数型スタむルのプログラムを曞く際の基瀎ずなる重芁な Python 機胜から芋おいきたしょう: むテレヌタです。

むテレヌタは連続デヌタを衚珟するオブゞェクトです; このオブゞェクトは䞀床に䞀぀の芁玠ず぀デヌタを返したす。 Python のむテレヌタは __next__() ずいう、匕数を取らず次の芁玠を返すメ゜ッドを必ずサポヌトしおいたす。デヌタストリヌムに芁玠が残っおいない堎合、 __next__() は必ず StopIteration 䟋倖を出したす。ただし、むテレヌタの長さは有限である必芁はありたせん; 無限のストリヌムを生成するむテレヌタを曞くずいうのもたったく理に適ったこずです。

ビルトむンの iter() 関数は任意のオブゞェクトを受けお、 その䞭身や芁玠を返すむテレヌタを返そうずしたす。匕数のオブゞェクトが むテレヌタを䜜れないずきは TypeError を投げたす。Python の ビルトむンなデヌタ型にもいく぀かむテレヌタ化のできるものがあり、 䞭でもよく䜿われるのはリストず蟞曞です。むテレヌタを䜜れる オブゞェクトは iterable オブゞェクトず呌ばれたす。

手を動かしおむテレヌタ化の実隓をしおみたしょう:

>>> L = [1, 2, 3]
>>> it = iter(L)
>>> it
<...iterator object at ...>
>>> it.__next__()  # same as next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

Python は色々な文脈でむテラブルなオブゞェクトを期埅したすが、 最も重芁なのは for 文です。 for X in Y ずいう文の Y は、 むテレヌタか、あるいは iter() でむテレヌタを䜜れるオブゞェクトである必芁がありたす。次の二぀は同じ意味になりたす:

for i in iter(obj):
    print(i)

for i in obj:
    print(i)

むテレヌタは list() や tuple() ずいったコンストラクタ関数を䜿っおリストやタプルに具珟化するこずができたす:

>>> L = [1, 2, 3]
>>> iterator = iter(L)
>>> t = tuple(iterator)
>>> t
(1, 2, 3)

シヌケンスのアンパックもむテレヌタに察応しおいたす: むテレヌタが N 個の芁玠を返すずいうこずが事前にわかっおいれば、N-タプルにアンパックするこずができたす:

>>> L = [1, 2, 3]
>>> iterator = iter(L)
>>> a, b, c = iterator
>>> a, b, c
(1, 2, 3)

ビルトむン関数の max() や min() なども、むテレヌタ䞀぀だけを匕数に取っお最倧・最小の芁玠を返すこずができたす。 "in" や "not in" 挔算子もむテレヌタに察応しおいたす: X in iterator は、そのむテレヌタから返るストリヌムに X があれば真です。ですからむテレヌタが無限長だず、圓然ながら問題に盎面したす; max(), min() はい぀たでも戻っお来たせんし、 芁玠 X がストリヌムに出おこなければ "in", "not in" オペレヌタも戻りたせん。

むテレヌタは次に進むこずしかできたせんのでご泚意ください; 前の芁玠を手に入れたり、むテレヌタをリセットしたり、コピヌを䜜ったりする方法はありたせん。むテレヌタがオブゞェクトずしおそうした远加機胜を 持぀こずはできたすが、プロトコルでは __next__() メ゜ッドのこずしか指定されおいたせん。ですから関数はむテレヌタの出力を䜿い尜くしお したうかもしれたせんし、同じストリヌムに䜕か別のこずをする 必芁があるなら新しいむテレヌタを䜜らなくおはいけたせん。

むテレヌタ察応のデヌタ型¶

リストやタプルがむテレヌタに察応しおいる方法に぀いおは既に芋たしたが、実のずころ Python のシヌケンス型はどれでも、たずえば文字列なども、自動でむテレヌタ生成に察応しおいたす。

蟞曞に察しお iter() するず、蟞曞のキヌでルヌプを回すむテレヌタが返されたす:

>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
...      'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
>>> for key in m:
...     print(key, m[key])
Jan 1
Feb 2
Mar 3
Apr 4
May 5
Jun 6
Jul 7
Aug 8
Sep 9
Oct 10
Nov 11
Dec 12

Python 3.7 から、蟞曞の反埩順序は挿入順序ず同じであるこずが保蚌されおいるこずに泚意しおください。 以前のバヌゞョンでは、その振る舞いは仕様が定められおおらず、実装ごずに異なるこずがありたした。

蟞曞は iter() を適甚するずキヌでルヌプを回したすが、蟞曞には他のむテレヌタを返すメ゜ッドもありたす。明瀺的に倀、あるいはキヌず倀のペアでむテレヌトしたければ、values(), items() ずいうメ゜ッドでむテレヌタを䜜るこずができたす。

逆に dict() コンストラクタは、有限な (key, value) タプルのストリヌムを返すむテレヌタを受け入れるこずができたす:

>>> L = [('Italy', 'Rome'), ('France', 'Paris'), ('US', 'Washington DC')]
>>> dict(iter(L))
{'Italy': 'Rome', 'France': 'Paris', 'US': 'Washington DC'}

ファむルも、最埌の行たで readline() メ゜ッドを呌んでいくこずでむテレヌタ化に察応しおいたす。぀たりこうやっおファむルの各行を読んでいくこずができるわけです:

for line in file:
    # do something for each line
    ...

セットはむテラブルを受け取れたすし、そのセットの芁玠でむテレヌトするこずもできたす:

>>> S = {2, 3, 5, 7, 11, 13}
>>> for i in S:
...     print(i)
2
3
5
7
11
13

ゞェネレヌタ匏ずリスト内包衚蚘¶

むテレヌタの出力に察しおよく䜿う操䜜トップ 2 は、(1) ひず぀ず぀党芁玠に操䜜を実行する、および (2) 条件に合う芁玠でサブセットを䜜る、です。たずえば文字列のリストなら、各行のうしろに付いた邪魔なホワむトスペヌスを削りたいずか、特定の文字列を含む郚分をピックアップしたいなどず思うかもしれたせん。

リスト内包衚蚘ずゞェネレヌタ匏 (略しお「listcomp」ず「genexp」) は、そうした操䜜向けの簡朔な衚蚘方法です。これは関数型プログラミング蚀語 Haskell (https://www.haskell.org/) にむンスパむアされたした。文字列のストリヌムからホワむトスペヌスをすべお削るのは次のコヌドでできたす:

>>> line_list = ['  line 1\n', 'line 2  \n', ' \n', '']

>>> # Generator expression -- returns iterator
>>> stripped_iter = (line.strip() for line in line_list)

>>> # List comprehension -- returns list
>>> stripped_list = [line.strip() for line in line_list]

特定の芁玠だけを遞び出すのは "if" 条件匏を付けるこずで可胜です:

>>> stripped_list = [line.strip() for line in line_list
...                  if line != ""]

リスト内包衚蚘を䜿うず Python リストが返っお来たす; stripped_list は実行結果の行が入ったリストであっお、むテレヌタではありたせん。ゞェネレヌタ匏はむテレヌタを返し、これだず必芁に応じおだけ倀を算出したすので、すべおの倀を䞀床に出す必芁がありたせん。぀たりリスト内包衚蚘のほうは、無限長ストリヌムや膚倧なデヌタを返すようなむテレヌタを扱う際には、あたり圹に立たないずいうこずです。そういった状況ではゞェネレヌタ匏のほうが奜たしいず蚀えたす。

ゞェネレヌタ匏は䞞括匧 "()" で囲たれ、リスト内包衚蚘は角括匧 "[]" で囲たれたす。ゞェネレヌタ匏の圢匏は次のずおりです:

( expression for expr in sequence1
             if condition1
             for expr2 in sequence2
             if condition2
             for expr3 in sequence3
             ...
             if condition3
             for exprN in sequenceN
             if conditionN )

リスト内包衚蚘も、倖偎の括匧が違うだけ (䞞ではなく角括匧) で、あずは同じです。

生成される出力は expression 郚分の倀を芁玠ずしお䞊べたものになりたす。 if 節はすべお、なくおも倧䞈倫です; あれば condition が真のずきだけ expression が評䟡されお出力に远加されたす。

ゞェネレヌタ匏は垞に括匧の䞭に曞かなければなりたせんが、関数コヌルの目印になっおいる括匧でも倧䞈倫です。関数にすぐ枡すむテレヌタを䜜りたければこう曞けるのです:

obj_total = sum(obj.count for obj in list_all_objects())

for...in 節は耇数぀なげられたすが、どれにも、むテレヌトするためのシヌケンスが含たれおいたす。それらのシヌケンスは䞊行しお ではなく 、巊から右ぞ順番にむテレヌトされるので、長さが同じである必芁はありたせん。 sequence1 の各芁玠ごずに毎回最初から sequence2 をルヌプで回すのです。その埌 sequence1 ず sequence2 から出た芁玠ペアごずに、 sequence3 でルヌプしたす。

別の曞き方をするず、リスト内包衚蚘やゞェネレヌタ匏は次の Python コヌドず同じ意味になりたす:

for expr1 in sequence1:
    if not (condition1):
        continue   # Skip this element
    for expr2 in sequence2:
        if not (condition2):
            continue   # Skip this element
        ...
        for exprN in sequenceN:
            if not (conditionN):
                continue   # Skip this element

            # Output the value of
            # the expression.

぀たり、耇数の for...in 節があっお if がないずきの最終出力は、長さが各シヌケンス長の積に等しくなるずいうこずです。長さ 3 のリスト二぀なら、出力リストの長さは 9 芁玠です:

>>> seq1 = 'abc'
>>> seq2 = (1, 2, 3)
>>> [(x, y) for x in seq1 for y in seq2]
[('a', 1), ('a', 2), ('a', 3),
 ('b', 1), ('b', 2), ('b', 3),
 ('c', 1), ('c', 2), ('c', 3)]

Python の文法に曖昧さを玛れ蟌たせないように、 expression でタプルを䜜るなら括匧で囲わなくおはなりたせん。䞋にあるリスト内包衚蚘で、最初のは構文゚ラヌですが、二番目は有効です:

# Syntax error
[x, y for x in seq1 for y in seq2]
# Correct
[(x, y) for x in seq1 for y in seq2]

ゞェネレヌタ (generator)¶

ゞェネレヌタは、むテレヌタを曞く䜜業を簡単にする、特殊な関数です。暙準的な関数は倀を蚈算しお返したすが、ゞェネレヌタが返すのは、䞀連の倀を返すむテレヌタです。

Python や C の暙準的な関数コヌルに぀いおは、よくご存じに違いありたせん。関数を呌ぶず、ロヌカル倉数を䜜るプラむベヌトな名前空間ができたすね。その関数が return 文たで来るず、ロヌカル倉数が砎壊されおから、返り倀が呌び出し元に返りたす。次に同じ関数をもう䞀床呌ぶず、新しいプラむベヌト名前空間に新芏のロヌカル倉数が䜜られるのです。しかし、関数を出るずきにロヌカル倉数を捚おなければどうなるでしょうか。その出おいったずころから関数を続行できたずしたら、どうでしょう。これこそゞェネレヌタが提䟛する機胜です; すなわち、ゞェネレヌタは続行できる関数ず考えるこずができたす。

ごく単玔なゞェネレヌタ関数の䟋がこちらにありたす:

>>> def generate_ints(N):
...    for i in range(N):
...        yield i

yield キヌワヌドを含む関数はすべおゞェネレヌタ関数です; Python の bytecode コンパむラがこれを怜出しお、特別な方法でコンパむルしおくれるのです。

ゞェネレヌタ関数は、呌ばれたずきに䞀回だけ倀を返すのではなく、むテレヌタ プロトコルに察応したオブゞェクトを返したす。䞊の䟋で yield を実行したずき、 ゞェネレヌタは return 文のようにしお i の倀を出力したす。 yield ず return 文の倧きな違いは、 yield に到達した段階でゞェネレヌタの実行状態が䞀時停止になっお、ロヌカル倉数が保存される点です。 次回そのゞェネレヌタの __next__() を呌ぶず、そこから関数が実行を再開したす。

䞊蚘 generate_ints() ゞェネレヌタの䜿甚䟋はこちらです:

>>> gen = generate_ints(3)
>>> gen
<generator object generate_ints at ...>
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
Traceback (most recent call last):
  File "stdin", line 1, in <module>
  File "stdin", line 2, in generate_ints
StopIteration

同じく for i in generate_ints(5) や a, b, c = generate_ints(3) ずいった曞き方もできたす。

ゞェネレヌタ関数の䞭では、return value は __next__() メ゜ッドから送出された StopIteration(value) を匕き起こしたす。これが発生した堎合や、関数の終わりに到達した堎合は、倀の生成が終了しおゞェネレヌタヌがそれ以䞊の倀を返さない。

自分でクラスを曞いお、ゞェネレヌタで蚀うずころのロヌカル倉数をむンスタンス倉数ずしお党郚保管しおおけば、同じ効果を埗るこずは可胜です。たずえば敎数のリストを返すのは、 self.count を 0 にしお、 __next__() メ゜ッドが self.count をむンクリメントしお返すようにすればできたす。しかしながら、ある皋床耇雑なゞェネレヌタになっおくるず、同じこずをするクラスを曞くのは栌段にややこしいこずになりたす。

Python のラむブラリに含たれおいるテストスむヌト Lib/test/test_generators.py には、ほかにも興味深い䟋が数倚く入っおいたす。これは二分朚の通りがけ順 (in-order) 探玢を再垰で実装したゞェネレヌタです。

# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
    if t:
        for x in inorder(t.left):
            yield x

        yield t.label

        for x in inorder(t.right):
            yield x

ほかにも test_generators.py には、N-Queens 問題 (N×N コマのチェス盀に、互いに攻撃できないような配眮で N 個のクむヌンを眮く) やナむト・ツアヌ (N×N 盀の党コマをナむトが䞀床ず぀通るような経路を探す) の解を出す䟋が入っおいたす。

ゞェネレヌタに倀を枡す¶

Python 2.4 たでのゞェネレヌタは出力するこずしかできたせんでした。ゞェネレヌタのコヌドを実行しおむテレヌタを䜜っおしたったあずで、その関数を再開するずきに新しい情報を枡す手段はなかったのです。ゞェネレヌタがグロヌバル倉数を芋るようにしたり、ミュヌタブルなオブゞェクトを枡しおおいお呌び出し元であずからそれを倉曎したり、ずいったハックは可胜でしたが、どれもゎチャゎチャしおいたすね。

Python 2.5 で、ゞェネレヌタに倀を枡す簡単な手段ができたした。 yield が、倉数に代入したり挔算したりできる倀を返す匏になったのです:

val = (yield i)

䞊のように、返り倀で䜕かをするずきは yield 匏の前埌に 必ず 括匧を付けるようお勧めしたす。括匧は垞に必芁なわけではありたせんが、どんなずき付けなくお良いのかを芚えおおくより、い぀も付けおおくほうが楜ですから。

(PEP 342 がその芏則を正確に説明しおいたすが、それによるず yield-匏は、代入匏で右蟺のトップレベルにあるずき以倖はい぀も括匧を付ける必芁がありたす。぀たり val = yield i ずは曞けたすが、 val = (yield i) + 12 のように挔算子があるずきは括匧を䜿わなくおはいけたせん。)

ゞェネレヌタに倀を送るには send(value) メ゜ッドを呌びたす。 するずゞェネレヌタのコヌドが実行を再開し、 yield 匏がその倀を返すのです。ふ぀うの __next__() メ゜ッドを呌ぶず、 yield は None を返したす。

䞋にあるのは 1 ず぀増える単玔なカりンタですが、内郚カりンタの倀を倉曎するこずができるようになっおいたす。

def counter(maximum):
    i = 0
    while i < maximum:
        val = (yield i)
        # If value provided, change counter
        if val is not None:
            i = val
        else:
            i += 1

そしおカりンタ倉曎の䟋がこちらです:

>>> it = counter(10)
>>> next(it)
0
>>> next(it)
1
>>> it.send(8)
8
>>> next(it)
9
>>> next(it)
Traceback (most recent call last):
  File "t.py", line 15, in <module>
    it.next()
StopIteration

yield が None を返すこずはよくあるのですから、そうなっおいないかどうか必ずチェックしおおくべきです。ゞェネレヌタ関数を再開するために䜿うメ゜ッドが send() しかないのだず確定しおるのでない限り、匏の倀をそのたた䜿っおはいけたせん。

ゞェネレヌタには、 send() のほかにもメ゜ッドが二぀ありたす:

  • throw(value) はゞェネレヌタ内で䟋倖を投げるために䜿いたす; その䟋倖はゞェネレヌタの実行が停止したずころの yield 匏によっお投げられたす。

  • close() sends a GeneratorExit exception to the generator to terminate the iteration. On receiving this exception, the generator's code must either raise GeneratorExit or StopIteration; catching the exception and doing anything else is illegal and will trigger a RuntimeError. close() will also be called by Python's garbage collector when the generator is garbage-collected.

    GeneratorExit が起こったずきにクリヌンアップ䜜業をする必芁があるなら、 GeneratorExit を捕捉するのではなく try: ... finaly: するようお勧めしたす。

これらの倉曎の合わせ技で、ゞェネレヌタは情報の䞀方的な生産者から、生産者か぀消費者ずいう存圚に倉貌を遂げたのです。

ゞェネレヌタは コルヌチン ずいう、より䞀般化された圢匏のサブルヌチンにもなりたす。サブルヌチンは䞀カ所 (関数の冒頭) から入っお別の䞀カ所 (return 文) から出るだけですが、コルヌチンはいろいろな堎所 (yield 文) から入ったり出たり再開したりできるのです。

組み蟌み関数 (built-in function)¶

よくむテレヌタず䞀緒に䜿うビルトむン関数に぀いお、もっず詳しく芋おいきたしょう。

Python のビルトむン関数 map() ず filter() は、機胜がゞェネレヌタ匏ず重耇しおいたす:

map(f, iterA, iterB, ...) は以䞋のシヌケンスのむテレヌタを返したす

f(iterA[0], iterB[0]), f(iterA[1], iterB[1]), f(iterA[2], iterB[2]), ....

>>> def upper(s):
...     return s.upper()
>>> list(map(upper, ['sentence', 'fragment']))
['SENTENCE', 'FRAGMENT']
>>> [upper(s) for s in ['sentence', 'fragment']]
['SENTENCE', 'FRAGMENT']

もちろん、リスト内包衚蚘でも同じ結果が埗られたす。

filter(predicate, iter) はある条件を満たす芁玠に枡るむテレヌタを返すので、同様にリスト内包衚蚘で再珟できたす。 predicate は、ある条件に察する真停倀を返す関数です; filter() で䜿うには、その関数の匕数は䞀぀でなければなりたせん。

>>> def is_even(x):
...     return (x % 2) == 0
>>> list(filter(is_even, range(10)))
[0, 2, 4, 6, 8]

これはリスト内包衚蚘でも曞けたす:

>>> list(x for x in range(10) if is_even(x))
[0, 2, 4, 6, 8]

enumerate(iter, start=0) はむテラブルの芁玠に順番に番号を振っおいき、(start から数え始めたずきの) 番号ずそれぞれの芁玠を含む 2 タプルを返したす。

>>> for item in enumerate(['subject', 'verb', 'object']):
...     print(item)
(0, 'subject')
(1, 'verb')
(2, 'object')

enumerate() はよく、リストに察しおルヌプさせお、条件に合う所に印を付けおいくずきに䜿われたす:

f = open('data.txt', 'r')
for i, line in enumerate(f):
    if line.strip() == '':
        print('Blank line at line #%i' % i)

sorted(iterable, key=None, reverse=False) はむテラブルの芁玠をすべお集めたリストを䜜り、゜ヌトしお返したす。 匕数 key および reverse は、リストの sort() メ゜ッドにそのたた枡されたす。

>>> import random
>>> # Generate 8 random numbers between [0, 10000)
>>> rand_list = random.sample(range(10000), 8)
>>> rand_list
[769, 7953, 9828, 6431, 8442, 9878, 6213, 2207]
>>> sorted(rand_list)
[769, 2207, 6213, 6431, 7953, 8442, 9828, 9878]
>>> sorted(rand_list, reverse=True)
[9878, 9828, 8442, 7953, 6431, 6213, 2207, 769]

(゜ヌトに関する詳现な論議は ゜ヌトのテクニック を参照)

組み蟌みの any(iter) および all(iter) は iterable の倀の真停を調べたす。 any() は芁玠のどれかが真倀なら True を返し、 all() は芁玠が党お真倀なら True を返したす:

>>> any([0, 1, 0])
True
>>> any([0, 0, 0])
False
>>> any([1, 1, 1])
True
>>> all([0, 1, 0])
False
>>> all([0, 0, 0])
False
>>> all([1, 1, 1])
True

zip(iterA, iterB, ...) はそれぞれの iterable から1぀の芁玠を取り、それらをタプルに入れお返したす:

zip(['a', 'b', 'c'], (1, 2, 3)) =>
  ('a', 1), ('b', 2), ('c', 3)

It doesn't construct an in-memory list and exhaust all the input iterators before returning; instead tuples are constructed and returned only if they're requested. (The technical term for this behaviour is lazy evaluation.)

このむテレヌタの甚途には、すべお同じ長さのむテラブルを想定しおいたす。長さが違っおいれば、出力されるストリヌムは䞀番短いむテラブルず同じ長さになりたす。

zip(['a', 'b'], (1, 2, 3)) =>
  ('a', 1), ('b', 2)

ずは蚀え、これをやっおしたうず長いむテレヌタから芁玠をひず぀無駄に倚く取っお捚おおしたうかもしれたせんので、やめおおいたほうが良いです。その捚おられた芁玠を抜かしおしたう危険があるので、もうそのむテレヌタはそれ以䞊䜿えなくなっおしたいたす。

itertools モゞュヌル¶

itertools モゞュヌルには、よく䜿うむテレヌタや、むテレヌタ同士の連結に䜿う関数がたくさん含たれおいたす。この章では、そのモゞュヌルの内容を小さな䟋で玹介しおいきたいず思いたす。

このモゞュヌルの関数を倧たかに分けるずこうなりたす:

  • 既存のむテレヌタに基づいお新しいむテレヌタを䜜る関数。

  • むテレヌタの芁玠を匕数ずしお扱う関数。

  • むテレヌタの出力から䞀郚を取り出す関数。

  • むテレヌタの出力をグルヌプ分けする関数。

新しいむテレヌタを䜜る¶

itertools.count(start, step) は倀の間隔が䞀定の無限ストリヌムを返したす。 オプションで開始する数 (デフォルトは 0) や数どうしの間隔 (デフォルトは 1) を䞎えられたす:

itertools.count() =>
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
itertools.count(10) =>
  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...
itertools.count(10, 5) =>
  10, 15, 20, 25, 30, 35, 40, 45, 50, 55, ...

itertools.cycle(iter) は䞎えられたむテラブルの内容をコピヌしお、 その芁玠を最初から最埌たで無限に繰り返しおいくむテレヌタを返したす。

itertools.cycle([1, 2, 3, 4, 5]) =>
  1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...

itertools.repeat(elem, [n]) は、䞎えられた芁玠を n 回返したすが、 n がなければ氞遠に返し続けたす。

itertools.repeat('abc') =>
  abc, abc, abc, abc, abc, abc, abc, abc, abc, abc, ...
itertools.repeat('abc', 5) =>
  abc, abc, abc, abc, abc

itertools.chain(iterA, iterB, ...) takes an arbitrary number of iterables as input, and returns all the elements of the first iterator, then all the elements of the second, and so on, until all of the iterables have been exhausted.

itertools.chain(['a', 'b', 'c'], (1, 2, 3)) =>
  a, b, c, 1, 2, 3

itertools.islice(iter, [start], stop, [step]) は、むテレヌタの スラむスをストリヌムで返したす。 stop 匕数だけだず、最初の stop 個の芁玠を返したす。開始むンデックスを枡すず stop-start 個で、 step の倀も枡せばそれに応じお芁玠を抜かしたす。Python における文字列やリストのスラむスずは違っお、マむナスの倀は start, stop, step に䜿えたせん。

itertools.islice(range(10), 8) =>
  0, 1, 2, 3, 4, 5, 6, 7
itertools.islice(range(10), 2, 8) =>
  2, 3, 4, 5, 6, 7
itertools.islice(range(10), 2, 8, 2) =>
  2, 4, 6

itertools.tee(iter, [n]) はむテレヌタを耇補したす; 元のむテレヌタの内容を同じように返す、独立した n 個のむテレヌタを返すのです。 n の倀は、指定しなければ既定が 2 になっおいたす。耇補するには元のむテレヌタの内容を䞀郚保存しおおく必芁がありたすから、倧きなむテレヌタから耇補したうちの䞀぀が他よりも進んでいっおしたうず、倧量のメモリを消費するこずがありたす。

itertools.tee( itertools.count() ) =>
   iterA, iterB

where iterA ->
   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...

and   iterB ->
   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...

芁玠に察しお関数を呌ぶ¶

いた䜿った operator モゞュヌルには、Python の挔算子に察応する関数が入っおいたす。いく぀か䟋を挙げるず、 operator.add(a, b) (二぀の倀を加算)、 operator.ne(a, b) (a != b ず同じ)、 operator.attrgetter('id') (.id 属性を取埗するコヌラブルを返す) ずいった関数です。

itertools.starmap(func, iter) は、むテラブルがタプルのストリヌムを返すずみなしお、 そのタプルを匕数に䜿っお func を呌びたす:

itertools.starmap(os.path.join,
                  [('/bin', 'python'), ('/usr', 'bin', 'java'),
                   ('/usr', 'bin', 'perl'), ('/usr', 'bin', 'ruby')])
=>
  /bin/python, /usr/bin/java, /usr/bin/perl, /usr/bin/ruby

芁玠を遞択する¶

さらに別のグルヌプずしお、述語 (predicate) に基づいおむテレヌタの芁玠からサブセットを遞び出す関数がありたす。

itertools.filterfalse(predicate, iter) は filter() ずは反察に、 述語が停を返す芁玠をすべお返したす:

itertools.filterfalse(is_even, itertools.count()) =>
  1, 3, 5, 7, 9, 11, 13, 15, ...

itertools.takewhile(predicate, iter) は述語が真を返しおいる間だけ芁玠を返したす。䞀床でも述語が停を返すず、むテレヌタは出力終了の合図をしたす。

def less_than_10(x):
    return x < 10

itertools.takewhile(less_than_10, itertools.count()) =>
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9

itertools.takewhile(is_even, itertools.count()) =>
  0

itertools.dropwhile(predicate, iter) は、述語が真を返しおいるうちは芁玠を無芖し、停になっおから残りの出力をすべお返したす。

itertools.dropwhile(less_than_10, itertools.count()) =>
  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...

itertools.dropwhile(is_even, itertools.count()) =>
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...

itertools.compress(data, selectors) takes two iterators and returns only those elements of data for which the corresponding element of selectors is true, stopping whenever either one is exhausted:

itertools.compress([1, 2, 3, 4, 5], [True, True, False, False, True]) =>
   1, 2, 5

組合せ関数¶

itertools.combinations(iterable, r) は、 iterable から r-tuple 遞択する党おの組み合わせを提䟛するむテレヌタを返したす

itertools.combinations([1, 2, 3, 4, 5], 2) =>
  (1, 2), (1, 3), (1, 4), (1, 5),
  (2, 3), (2, 4), (2, 5),
  (3, 4), (3, 5),
  (4, 5)

itertools.combinations([1, 2, 3, 4, 5], 3) =>
  (1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5),
  (2, 3, 4), (2, 3, 5), (2, 4, 5),
  (3, 4, 5)

それぞれのタプル内では、芁玠は iterable がそれを返したのず同じ順序を保ちたす。䟋えば䞊の䟋であれば、 1 はい぀でも 2, 3, 4, 5 の前に来たす。䌌たような関数に itertools.permutations(iterable, r=None) があり、こちらはこの順序に぀いおの制玄がなく、 r 個遞択する党おの順列を返したす。

itertools.permutations([1, 2, 3, 4, 5], 2) =>
  (1, 2), (1, 3), (1, 4), (1, 5),
  (2, 1), (2, 3), (2, 4), (2, 5),
  (3, 1), (3, 2), (3, 4), (3, 5),
  (4, 1), (4, 2), (4, 3), (4, 5),
  (5, 1), (5, 2), (5, 3), (5, 4)

itertools.permutations([1, 2, 3, 4, 5]) =>
  (1, 2, 3, 4, 5), (1, 2, 3, 5, 4), (1, 2, 4, 3, 5),
  ...
  (5, 4, 3, 2, 1)

r を䞎えない堎合は iterable の長さが䜿われたす。぀たり iterable の党おの芁玠を遞んだ順列を返したす。

これらの関数が生成する組み合わせは、䜍眮が基準ですので、 iterable の内容が䞀意でなくずも良いこずに泚目しおください:

itertools.permutations('aba', 3) =>
  ('a', 'b', 'a'), ('a', 'a', 'b'), ('b', 'a', 'a'),
  ('b', 'a', 'a'), ('a', 'a', 'b'), ('a', 'b', 'a')

党く同じタプル ('a', 'a', 'b') が 2床珟れおいたすが、これは 2぀の 'a' が別の䜍眮からのものだからです。

itertools.combinations_with_replacement(iterable, r) 関数は別の制玄を取り払いたす: 䞀回の遞択で同じ芁玠を繰り返し遞んでも良い。抂念的には、それぞれのタプルの最初のものずしお䞀぀芁玠が遞ばれ、続いお 2぀目の遞択のかわりにそれで眮き換わりたす

itertools.combinations_with_replacement([1, 2, 3, 4, 5], 2) =>
  (1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
  (2, 2), (2, 3), (2, 4), (2, 5),
  (3, 3), (3, 4), (3, 5),
  (4, 4), (4, 5),
  (5, 5)

芁玠をグルヌプ分けする¶

最埌に議題に䞊げる関数 itertools.groupby(iter, key_func=None) は、 これたでで最も耇雑です。 key_func(elem) は、むテラブルから返っおきた芁玠それぞれのキヌ倀を蚈算する関数です。この関数が指定されおいなければ、 キヌは単に各芁玠そのものになりたす。

groupby() は、元になるむテラブルから同じキヌ倀を持぀連続する芁玠を集めお、キヌ倀ずそのキヌに察応する芁玠のむテレヌタの 2-タプルのストリヌムを返したす。

city_list = [('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL'),
             ('Anchorage', 'AK'), ('Nome', 'AK'),
             ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ'),
             ...
            ]

def get_state(city_state):
    return city_state[1]

itertools.groupby(city_list, get_state) =>
  ('AL', iterator-1),
  ('AK', iterator-2),
  ('AZ', iterator-3), ...

where
iterator-1 =>
  ('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL')
iterator-2 =>
  ('Anchorage', 'AK'), ('Nome', 'AK')
iterator-3 =>
  ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ')

groupby() は、元になるむテラブルの内容がキヌ倀で゜ヌトされた状態で䞎えられるこずを想定しおいたす。ここで、返されるむテレヌタ自䜓も元のむテラブルを䜿うずいうこずに泚意しおください。そのため、 iterator-1 の結果を読み終わるたでは iterator-2 ずそれに察応するキヌ倀を芁求するこずはできたせん。

functools モゞュヌル¶

functools モゞュヌルには、高階関数がいく぀か入っおいたす。 高階関数 ずは、入力ずしお関数を受け取っお新たな関数を返す関数です。このモゞュヌルで䞀番䟿利なツヌルは functools.partial() 関数です。

関数型スタむルのプログラムでは時折、既存の関数から䞀郚のパラメヌタを埋めた倉皮を䜜りたくなるこずがありたす。Python の関数 f(a, b, c) ずいうものがあるずしおください; f(1, b, c) ず同じ意味の g(b, c) ずいう関数を䜜りたくなるこずがありたす; ぀たり f() のパラメヌタを䞀぀埋めるわけです。これは「関数の郚分適甚」ず呌ばれおいたす。

partial() のコンストラクタは (function, arg1, arg2, ..., kwarg1=value1, kwarg2=value2) ずいう匕数を取りたす。できあがったオブゞェクトはコヌラブルなので、それを呌べば、匕数の埋たった状態で function を実行したのず同じこずになりたす。

以䞋にあるのは、小さいけれども珟実的な䞀぀の䟋です:

import functools

def log(message, subsystem):
    """Write the contents of 'message' to the specified subsystem."""
    print('%s: %s' % (subsystem, message))
    ...

server_log = functools.partial(log, subsystem='server')
server_log('Unable to open socket')

functools.reduce(func, iter, [initial_value]) cumulatively performs an operation on all the iterable's elements and, therefore, can't be applied to infinite iterables. func must be a function that takes two elements and returns a single value. functools.reduce() takes the first two elements A and B returned by the iterator and calculates func(A, B). It then requests the third element, C, calculates func(func(A, B), C), combines this result with the fourth element returned, and continues until the iterable is exhausted. If the iterable returns no values at all, a TypeError exception is raised. If the initial value is supplied, it's used as a starting point and func(initial_value, A) is the first calculation.

>>> import operator, functools
>>> functools.reduce(operator.concat, ['A', 'BB', 'C'])
'ABBC'
>>> functools.reduce(operator.concat, [])
Traceback (most recent call last):
  ...
TypeError: reduce() of empty iterable with no initial value
>>> functools.reduce(operator.mul, [1, 2, 3], 1)
6
>>> functools.reduce(operator.mul, [], 1)
1

operator.add() を functools.reduce() で䜿うず、 iterable の党芁玠を合蚈するこずになりたす。これは䜿甚頻床が高いので、そのための sum() ずいうビルトむン関数がありたす:

>>> import functools, operator
>>> functools.reduce(operator.add, [1, 2, 3, 4], 0)
10
>>> sum([1, 2, 3, 4])
10
>>> sum([])
0

ずはいえ、倚くの堎合 functools.reduce() を䜿うよりは、単に for ルヌプを曞いたほうがわかりやすくなりたす:

import functools
# Instead of:
product = functools.reduce(operator.mul, [1, 2, 3], 1)

# You can write:
product = 1
for i in [1, 2, 3]:
    product *= i

関連する関数は itertools.accumulate(iterable, func=operator.add) です。この関数は同じ蚈算をしたすが、最終結果を返すのではなく、 accumulate() はそれぞれの䞭間結果を生成するむテレヌタが返り倀ずなりたす。

itertools.accumulate([1, 2, 3, 4, 5]) =>
  1, 3, 6, 10, 15

itertools.accumulate([1, 2, 3, 4, 5], operator.mul) =>
  1, 2, 6, 24, 120

operator モゞュヌル¶

operator モゞュヌルは、既に取り䞊げたしたが、Python の挔算子に察応する関数が入っおいるモゞュヌルです。関数型スタむルのコヌドにおいお、挔算を䞀぀実行するだけのくだらない関数を曞かずに枈むので、よく䞖話になりたす。

このモゞュヌルの関数を䞀郚だけ玹介したしょう:

  • 数孊挔算子: add(), sub(), mul(), floordiv(), abs(), ...

  • 論理挔算子: not_(), truth()

  • ビット挔算子: and_(), or_(), invert()

  • 比范: eq(), ne(), lt(), le(), gt(), ge()

  • オブゞェクト識別: is_(), is_not()

ちゃんずした䞀芧は operator モゞュヌルの文曞でご芧ください。

小さな関数ずラムダ匏¶

関数型スタむルのプログラムを曞いおいるず、述語ずしお働いたり、䜕らかの圢で芁玠を぀なぎ合わせたりするミニサむズの関数を必芁ずするこずがよくありたす。

ちょうど良い関数がビルトむンやモゞュヌルで存圚しおいれば、新しい関数を定矩する必芁はたったくありたせん:

stripped_lines = [line.strip() for line in lines]
existing_files = filter(os.path.exists, file_list)

しかし、欲しい関数がないなら曞くしかありたせん。そうした小さな関数を曞く方法の 䞀぀が lambda 匏です。 lambda は匕数ずしお耇数のパラメヌタず それを぀なぐ匏を取り、その匏の倀を返す無名の関数を䜜りたす:

adder = lambda x, y: x+y

print_assign = lambda name, value: name + '=' + str(value)

もう䞀぀の遞択肢は、ふ぀うに def 文で関数を定矩するだけです:

def adder(x, y):
    return x + y

def print_assign(name, value):
    return name + '=' + str(value)

どちらのほうが良いのでしょうか。それは奜みの問題です; 著者のスタむルずしおはできるだけ lambda を䜿わないようにしおいたす。

そのようにしおいる理由の䞀぀に、lambda は定矩できる関数が非垞に限られおいるずいう点がありたす。䞀぀の匏ずしお算出できる結果にしなければいけたせんので、if... elif... else や try... except のような分岐を持぀こずができないのです。lambda 文の䞭でたくさんのこずをやろうずしすぎるず、ごちゃごちゃしお読みにくい匏になっおしたいたす。さお、次のコヌドは䜕をしおいるでしょうか、玠早くお答えください!

import functools
total = functools.reduce(lambda a, b: (0, a[1] + b[1]), items)[1]

わかるにはわかるでしょうが、䜕がどうなっおいるのか玐解いおいくには時間がかかるはずです。短い def 文で入れ子にするず、少し芋通しが良くなりたすが:

import functools
def combine(a, b):
    return 0, a[1] + b[1]

total = functools.reduce(combine, items)[1]

でも単玔に for ルヌプにすれば良かったのです:

total = 0
for a, b in items:
    total += b

あるいは sum() ビルトむンずゞェネレヌタ匏でも良いですね:

total = sum(b for a, b in items)

倚くの堎合、 functools.reduce() を䜿っおいるずころは for ルヌプに 曞き盎したほうが芋やすいです。

Fredrik Lundh は以前 lambda 利甚のリファクタリングに関しお以䞋の指針を提案したこずがありたす:

  1. ラムダ関数を曞く。

  2. そのラムダが䞀䜓ぜんたい䜕をしおいるのかコメントで説明する。

  3. そのコメントをしばらく研究しお、本質をずらえた名前を考える。

  4. ラムダをその名前で def 文に曞き換える。

  5. コメントを消す。

著者はこの指針を本圓に気に入っおいたすが、こうしたラムダなしスタむルが他より優れおいるかどうかに぀いお、異論は認めたす。

曎新履歎ず謝蟞¶

The author would like to thank the following people for offering suggestions, corrections and assistance with various drafts of this article: Ian Bicking, Nick Coghlan, Nick Efford, Raymond Hettinger, Jim Jewett, Mike Krell, Leandro Lameiro, Jussi Salmela, Collin Winter, Blake Winton.

Version 0.1: posted June 30 2006.

Version 0.11: posted July 1 2006. Typo fixes.

Version 0.2: posted July 10 2006. Merged genexp and listcomp sections into one. Typo fixes.

Version 0.21: Added more references suggested on the tutor mailing list.

Version 0.30: Adds a section on the functional module written by Collin Winter; adds short section on the operator module; a few other edits.

参考資料¶

䞀般論¶

蚈算機プログラムの構造ず解釈、Harold AbelsonずGerald Jay Sussman、Julie Sussman著。この本は https://mitpress.mit.edu/sicp で芋れたす。この蚈算機科孊の叀兞的な教科曞では、2章ず3章でシヌケンスずストリヌムを䜿っおプログラム内のデヌタフロヌを敎理する方法に぀いお説明しおいたす。この本では䟋に Scheme を䜿っおいたすが、これらの章で説明されおいる蚭蚈アプロヌチの倚くは、関数型スタむルの Python コヌドにも適甚できたす。

https://defmacro.org/2006/06/19/fp.html: A general introduction to functional programming that uses Java examples and has a lengthy historical introduction.

https://en.wikipedia.org/wiki/Functional_programming: 関数プログラミングに関する䞀般的な内容の蚘事。

https://en.wikipedia.org/wiki/Coroutine: コルヌチンに関する蚘事。

https://en.wikipedia.org/wiki/Partial_application: 関数の郚分適甚の抂念に぀いおの蚘事。

https://en.wikipedia.org/wiki/Currying: カリヌ化の抂念に関する蚘事。

Python 特有の話¶

https://gnosis.cx/TPiP/: David Mertz's の本の最初の章 Text Processing in Python では文曞凊理のための関数プログラミングに぀いお議論しおいたす、この議論の節には "Utilizing Higher-Order Functions in Text Processing" ずいうタむトルが぀いおいたす。

Mertz also wrote a 3-part series of articles on functional programming for IBM's DeveloperWorks site; see part 1, part 2, and part 3,

Python 文曞¶

itertools モゞュヌルの文曞。

functools モゞュヌルに぀いおのドキュメント。

operator モゞュヌルの文曞。

PEP 289: "Generator Expressions"

PEP 342: "Coroutines via Enhanced Generators" describes the new generator features in Python 2.5.