Vercel LogoMagicode
0

無限ループから抜け出すプログラム

4 min read

Haskell で簡単に無限ループを作ってみる。

[ ]

もちろん他の言語でもwhile(1)とすれば簡単に作れるけど、大抵は無限ループから抜け出すためのbreakというおまじないが存在する。じゃあそれに相当する機能をHaskellでも作れるだろうか。

実際に作ってみた。

[ ]

大事なのはこの部分

[ ]

気持ちはこんな感じ

withBreakは独自定義なので後で解説する。forM_

[ ]

このような定義になっていて単にmapM_の引数をひっくり返したものである。liftIOwithBreakの中でIOを使うためのおまじないでwhenは第一引数の条件が満たされた時だけ第二引数を実行する。

forM_に渡されているリストは[1..]となっていて確かに無限リストなのだが実行結果をみるとbreakで処理は終わっている。

withBreakを見ていく。

[ ]

大事なのはrunContTcallCCの2つ。

[ ]

ContTは継続モナドと呼ばれるもので簡単に言うと後続する計算を扱うことが出来るモナド。継続に関しての詳しい説明は以下の文献を参考にしてください。

実はwithBreak $ \break -> ...breakは後続する計算をただ捨てているだけなのだ。 ここではwithBreakをさらに分解して何が起きているのかを理解することにする。まずContT

[ ]

この様に定義されていて、要するに(a -> m r) -> m rだ。callCCの実装は

[ ]

だが頑張ってContT(a -> m r) -> m rに書き換えると

[ ]

こうなる。型は複雑になったが実装はとても簡単だ。これを使うとwithBreak

[ ]

となる。最初のプログラム中でfに対応するものは\break -> ...の部分である。ここで初めてbreak

[ ]

のようなものであることがわかった。

もう一度継続モナドに戻る。ContTのモナドの実装は

[ ]

このようになっていて(>>=)(a -> m r) -> m rに合わせて書き直すと

[ ]

また型は複雑になったが実装は読みやすい。実装を眺めるとまずkを評価してからその値を使ってmを評価しているように見える。

準備が整ったので具体的にbreakの挙動を見るために以下のような式を考える。

[ ]

まず左側の>>を展開する

[ ]

次にbreakを展開してみる

[ ]

この時点で後続する計算cが消えていることがわかる。 最後に残った>>を展開する

[ ]

actionBは綺麗に消えてしまった。

最初のプログラムに戻ろう。

[ ]

Discussion

コメントにはログインが必要です。