MapReduceの語源が分かった(かも)

GoogleAppEngineで遊んでいたらMapReduceの語源が分かった(かも)。

pythonにmap()関数とreduce()関数が組み込み関数にあるのです。



■map(func, list, ...)
map関数はfunc関数をlistの全要素を引数として呼び出します。そしてその結果が格納されたリストを返します。つまり
 [func(list[0]), func(list[1]), func(list[2]), ...]
が戻ります。(可変長引数の説明は略)


■reduce(func, list, [initializer])
reduce関数はもっと面白くて、数学的帰納法みたいな感じです。
初期値があったほうがわかりやすいので、[initializer]をセットした場合で説明すると、
 N(0) = initializer
 N(x+1) = func(N(x),list[x]), x >= 0
 reduce(func, list): N(len(list))


…余計わかりにくくなった………!!


つまり平たく言うと
 func(func(func(initializer, list[0]), list[1]), list[2])
が戻ります。(listが3つの要素だった場合)


さらにpythonの場合、無名関数定義がこんなカンジで書けます。
lambda , , ...:
例:lambda a, b, c: a+b+c
※lambdaは固定(予約語)です


しかもこれは式なので、(通常の関数定義def〜は文)式中に書けます。つまりreduceのfuncの部分に書けます。
reduce(lambda x, y:x+y, [1, 2, 3])
とか書けちゃう。カンマやコロンがぐちゃぐちゃしててどこで区切れているのかpython初心者にはわかりづらすぎます!


ちなみにネット巡回しててreduce関数知りました。知らなかったらfor文で実装してただろうな…
↓Rubyの同種の関数injectについて
http://d.hatena.ne.jp/Hash/20090625/1245949183
http://d.hatena.ne.jp/kenkitii/20090114/ruby_inject
↓Rubyの同種の関数injectとPythonでのreduce関数の書きっぷりの比較
http://d.hatena.ne.jp/nullpobug/20080609/1212994658


map関数とreduce関数の詳細はオンラインドキュメントで
http://www.python.jp/doc/release/lib/built-in-funcs.html

MapReduceへの展開

言語レベルでのmap()とreduce()のリスト要素を分散されたサーバというオブジェクトの粒度と考えると、map()で各サーバに作業を開始させ、reduce()でその結果を1つづつ拾い集めていく感じになります。


そういえば社内で使ってる性能測定ツールは何気にMapReduceで作られてます。
MapReduceの概念が広まる前に作られたツールなのですが、そもそも割と素直な考え方なので当たり前かもしれません。

reduceの使いどころ(あまり正しい使い方ではなさそう)

最近図書館を活用することにしたのですが、近くの図書館はWebでの予約1回に対して5画面も通過しないといけないので一発予約できるアプリを作りました。
ただし、動的に変わる怪しげなhiddenパラメータがいっぱいあり、それらを一緒に送るようにしないといけないのでBeautifulSoupでスクレイピングして送ってあげています。この結果をリクエストパラメータの形式に変換するときにこんなコードを書くとキレイに書けるのです。


soup = BeautifulSoup(content)
payload = reduce(lambda x, y: x+y['name']+"="+y['value']+"&", soup.findAll('input', type='hidden'), "")


全然MapReduceまで行かないのですが、reduce関数だけでも結構役立ちます。