Pythonはさくっと書けてさくっと実行できて、しかも他のスクリプト言語と比べてチーム開発にもある程度耐えられるきちっとした構造ですばらしいですよね! 本記事は、 実際に ※ これはHaskell Advent Calendarの記事です。 本記事はUbuntu 14.04において、Python 2.7.6での動作結果をもとに記述しています。 Pythonプログラミングをしていると必ず出くわすエラー。 関数を呼び出したら結果が を明確に見分ける方法がPythonに提供されていないことに問題があります。 このようなわかりやすい例だとテストでバグが簡単に見つかるのですが、条件分岐の隅っこの方にこんなバグが隠れているのに気づかずに本番環境で用いてしまったら。。。 この例では、 また、 と記述することで、 を返します。 カッコが多くてまるでLISPのようになるこんな式も もっと単純に と書くことができます。 と記述することで、 を返します。 最近の多くのPythonistaは、 蛇足ですが、 これらはそれぞれ、異なる意味を持っています。 ほとんどの 詳細は省きますが、 関数を提供するだけなら、別に まずは 合計、200回のリスト要素への処理が発生しています。 もちろん、本来のプログラムの意図がわからなくなってしまっても良いのなら、次のように変換することもできます。 こうすることで、リスト要素への処理は150回に減り、実際に計算時間も減りますが、必ずしも常にこのような変換ができるとは限りませんし、可読性の低下にもつながります。 まずは、この関数定義を見てください。 高階関数とかを使おうとすると、型情報を日本語(またはその他の自然言語)で説明するのが億劫で仕方ないですね。。。 そこで関数定義の前に、こういった補助情報を記述してみることにしましょう。 を意味しています。 このようなエラーを吐き出して、ランタイムエラーを未然に防いでくれます。 mypyを使っても同様のType Hintは実現可能ですが、 Ubuntu 14.04 の場合、下記のコマンドで簡単にインストールできます。 残念ながら、 つまりなにが言いたいかというと、「PythonもいいけどHaskellもいいでしょ?」ということです。 古い情報を見て Haskellはめっちゃいい言語なのに、なかなか人を寄せ付けない雰囲気を醸し出しているので、Pythonみたいな感覚でさくっと使ってみてほしいなと思ってこういった形で紹介しました。 あとHaskell Advent CalendarでPythonの話をしてごめんなさい。 どうしても 厳密には拡張機能という表現は正しくありませんが、簡単のためこう表現することにします。 ↩Pythonへのバグの混入を防ぎ、可読性も向上させるAPPLe
はじめに
今日はそんなPythonプログラミングをもっと楽しいものにする珠玉のツールAPPLe
を紹介します。APPLe
はPythonに拡張機能1を提供する処理系で、今までと比べものにならないくらい可読性が高く、さらに厄介なバグの多くを排除したコードを書くことを助けてくれます。APPLe
の紹介として、なぜAPPLe
を使うと嬉しいかに終始しており、具体的なAPPLe
の機能にはあまり触れません。APPLe
を使う方法や、より詳しく学ぶ方法については最後にご紹介しているので、本記事を最初から通して読んでみて「いいな」と思ったら実際にAPPLe
を導入してみてくだい。
私が保守運用している会社でも内部的にAPPLe
を本番投入しているので、導入を検討の企業さまはお声掛けいただけるとなにかいいことがあるかもしれないです。環境
APPLeの使いドコロ
Noneなんてナンセンス
>>> "Hello " + foo()
TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'
None
になっていたため、ランタイムエラーになってしまっています。
そもそもNone
にならない関数None
になってしまうかもしれない関数
恐ろしいものです。APPLe
はこの問題を解決します。$ apple foo.py
foo.py:1:8:
Couldn't match expected type `[Char]'
with actual type `Maybe [Char]'
apple
コマンドはfoo.py
を静的解析し、ランタイムエラーをもたらす原因となるバグがない場合に限り、実際にそのコードを実行します。foo.py
にNone
に起因する問題があったため、実行前にエラーが表示されました。foo.py
スクリプトの1行目にNone
になるかもしれない値を返す関数があるのに、その返値をNone
にならない値としてあつかってしまったことが原因です。APPLe
にはfmap
およびmaybe
という構文があり、このNone
かもしれない値のあつかいを楽にしてくれます。fmap
fmap
はNone
をそのまま他の処理に渡したいときに便利です。(fmap f nullable)
nullable
がNone
のときにはNone
nullable
がその他の値の時にはその値にf
を適用した返値fmap
には、さらに便利な代替記法として>>=
と=<<
があります。ans = (fmap f4 (fmap f3 (fmap f2 (fmap f1 x))))
ans = f4 =<< f3 =<< f2 =<< f1 x
ans = f1 x >>= f2 >>= f3 >>= f4
まじいけてる。maybe
maybe
はNone
の場合のデフォルト値を設定できます。(maybe "who r u?" f nullable)
nullable
がNone
のときにはデフォルト値の"who r u?"
nullable
がその他の値の時にはその値にf
を適用した返値Goodbye for文 forever!
for
文なんて使いたがりませんよね?for
文はなぜよくないかfor
文のイケてなさの一端について述べておきます。for
文の問題点は、その可読性の低さにあります。
次のfor
文を使った3つのコードから、for
文がいかに可読性を損なうかをおさらいしましょう。xs = []
for n in ls:
xs.append(n+1)
print xs
xs = []
for n in ls:
if n % 2 == 0:
xs.append(n)
print xs
sum = 0
for n in ls:
sum += n
print sum
map.py
: リストのそれぞれの要素に対して同じ操作を行うfilter.py
: リストの中から条件を満たす要素のみを抜き出すreduce.py
: リストの左側の要素から順番に、これまでの要素の計算結果を用いた処理を行うfor
文はこのいずれかの処理を行うために使われていますが、コードが複雑になるとどういう意図なのか判然としなくなってしまう上に、余計な変数への再代入が何度も行われるため、バグを生む温床になってしまいます。
そのため、すてきなPythonistaのみなさんなら、map
, filter
, reduce
の関数を使った楽しい日々を送っていることでしょう。APPLe
の標準モジュールには、for
文を駆逐すべくfoldr
, scanl
, scanr
, iterate
などの便利な関数が用意されており、快適なPythonライフを過ごすことを可能にします。処理効率の向上
APPLe
を使わずとも、イケてるライブラリをimport
すれば良いだけです。APPLe
を使うと、可読性を保ちながら処理効率を向上させることができます。for
文を使わないと処理効率が問題になる場合について確認しましょう。filter(lambda x: x % 2 == 0, map(lambda x: x + 1, ls))
len(ls) == 100
として、この式について考えてみます。map(lambda x: x + 1, filter(lambda x: x % 2 == 1, ls))
APPLe
を使えば、そんな心配は無用です。
詳細はここでは省きますが、Lazy Evaluationという仕組みにより、最初の形式で記述しても、リスト要素への処理はfor
文を使うのと同等の150回で済みます。もっと型をつかったらいいじゃん
def sortBy(arg1, arg2) :
"""
HOFなソート関数.
@param arg1 比較可能な2つの値を引数として受け取り、GT, EQ, LTのいずれかを返す関数
@param arg2 arg1の関数の引数になりうる型の値のリスト
@return arg2をarg1の比較方法を用いて昇順に並べ替えた結果のリスト
"""
...
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
a -> b -> c -> d
は、a
, b
, c
の型の値をそれぞれ引数にとって、d
の型を返値として返すことを意味することにします。
つまり、sortBy
の記述では、a
a
a
のリストa
のリスト
日本語で書くよりも、事前にみんなでルールを決めて、形式的に記述した方がわかりやすいですよね?APPLe
は、プログラム中にこのような関数の型に関する記述を見つけると、静的解析時に、その型に違反している箇所を教えてくれます。
例えば、このsortBy
関数にString -> Int -> Ordering
という型を持つ関数を最初の引数に与えると、Couldn't match type `Int' with `[Char]'
Expected type: String -> String -> Ordering
Actual type: String -> Int -> Ordering
もちろん、こういった型情報は「この関数は不安だな」とか「日本語で説明するのめんどくさいな」みたいな場所にだけ書いて、他の関数には型情報を付加しなくても、APPLe
がよろしくやって、うまくエラーを見つけてくれます。APPLe
にはAlgebraic Data Typeを定義する仕組みもあり、より厳密なテストが可能です。APPLeをもっと知るには
sudo apt-get install -y ghc
sudo ln -s `which runghc` /usr/bin/apple
APPLe
にはPythonに対して下位互換性がなく、Haskellという独自の言語仕様に従っています。
シンボリックリンクを貼るのが面倒な方には、直接Haskellコンパイラを使うのがオススメです。
より詳細に勉強するにはすごいH本を買いましょう。haskell-platform
とかcabal
とかをいきなりインストールしてしまわないように、@nobsunさんの記事などを参照するといいと思います。おわりに
APPLe
はその本体を提供する公式サイトにある"An advanced purely-functional programming languag e"が命名の由来です。
以前、同じような動機で「Haskellは命令形の気持ちで書けるよ」という入門記事のようなものを書いたことがありますが、やっぱりPythonとかと比べて簡単に入門できる環境が整っていないのも事実です。
「内容がないよー」な記事ではないので許してください。APPLe
を使った仕事がしたくて、あなたが十分に優秀であるのであれば、弊社に連絡してください。
- 3日目の記事: type roleについて学ぶ
0 コメント:
コメントを投稿