JVM系言語比較にJythonが無いので書いてみた
新規記事:「関数型の考え方: 実にさまざまな変換処理」を公開いたしました ibm.co/P6AxBQ #Java #dWJapan
— developerWorks Japanさん (@dW_Japan) 10月 25, 2012
このお題だと「Jythonならでは」という特色は出ないですね。CPythonでもIronPythonでも同じコードになりそうだけど、まぁそこは他の言語との比較が目的ということで気にしないことにしましょう。
環境は ubuntu12.04 の apt で入れた Jython 2.5.1 です。Python で functional するのに 2.5 はギリギリセーフのラインですね。
まずは元ネタのクラスベースのコードを丸写し。
PythonでClassを作るとなんといっても目につくのはself, self, self ですね。それからインスタンススコープのメンバ変数はコンストラクタ __init__ の中でおもむろに self にくっつけるのが通例になってます。
Python の演算子は関数ではないので reduce を適用するには関数化が必要です。関数内やメソッド内でローカル関数が定義できますので add はローカル定義してみました。メソッド self.isFactor を高階関数にわたすのに小細工がいらないあたりは便利です。
次は遅延評価版。
遅延評価版 filter の ifilter を使うことで、フィルタの結果をジェネレータで返します。ジェネレータは他言語だとストリームとかシーケンスとか呼ばれることもあるあれです。partial は 部分的用を行う関数。
Python にはメソッドチェーンで処理を繋ぐ文化がなく、そのため連続するメソッドや関数の呼び出しを1つの式にまとめようとすると、呼び出しのネスト地獄に陥ります。それを解消するために func_thread を定義しています。これを使うと
foo(bar(hoge(piyo(x))))
という式を
func_thread(x, piyo, hoge, bar, foo)
と書けます。括弧が減るだけでなく評価の順序が左から右になるので読みやすいかと。Clojure に似たような式変形を行うマクロ ->> があり「スレッド」「スレッディング」と呼ばれています。ネーミングはそこからとりました。F# の |> も似てますがこちらは「パイプ」と呼ぶんでしたっけ。
元ネタサイトの TlPrimeNumberClassifier.isPrime でメソッドチェーンを使っている部分をこの関数スレッドディングで書き換えています。
次はクラス定義無しで関数型的に
ちょっとしたバッチ処理やデータ加工等の補助ツールが欲しい時、そういう目的だとクラス定義でかっちりとしたコードを書くのはまどろっこしいと感じます。手早くコードを書くスタイルだとこんなコードになるのでは無いでしょうか。私の普段のコードに一番近いです。(さすがにここまで徹底して lambda 縛りにはしませんけど)。
lambda にするメリットは return が不要になること。lambda と return は同じ6文字ですが、通常の関数定義だとさらに def と 仮引数を囲む括弧がありますのでトータルでは lambda の方が文字数が少ないですね。
ちなみに、全部スレッディングすると
さほど読みにくくは無いですね。
余談
reduce(lambda x,y: x+y, ジェネレータ)
は
sum(ジェネレータ)
でも同じです。ClojureやScalaにも合計関数 sum はあるはずですが、元ネタサイトでは reduce/inject/fold と高階関数に統一されているようなので、今回はあえてそちらに合わせました。