Edited at

fixで簡単にループを書く

More than 1 year has passed since last update.

fixは関数の最小不動点を求める関数です。

fix :: (a -> a) -> a

fix f = f (fix f)

例えば

> fix (const "hello")

"hello"

常に文字列"hello"を返す関数の不動点(入力と出力が同じになる値)は"hello"です。(参考: Haskell/不動点と再帰)

fixといえば無名関数で再帰を書く時に使われる例が有名だと思います

fact :: Int -> Int

fact = fix $ \f n -> if n == 0 then 1 else n * f (n-1)

再帰関数が最小不動点として構成できるというのは凄いですね。(参考: 再帰プログラムの意味論について)


fixで簡単にループを書く

fixは普段コードを書くときも便利に使えます。例えばユーザーからの入力を反転して出力する動作を"quit"という入力が来るまで繰り返すプログラムを考えましょう

import Control.Monad

import Data.Function

main :: IO ()
main = do
putStrLn "Start"
fix $ \loop -> do -- ループ開始
str <- getLine -- 入力待ち
unless (str == "quit") $ do -- "quit"じゃ無ければ次を実行
putStrLn (reverse str) -- 反転して表示
loop -- 繰り返し
putStrLn "Bye"

fixを使わない場合letを使って再帰関数を一旦定義してから実行するといった手順でもできますが、fixを使ったほうが定義と実行が一度にできるのでスッキリすると思います。好みの問題かもしれませんが :expressionless:


ループに初期値を与える

次に先ほどのプログラムに回数制限を設けてみます。"quit"と入力されなくても3回ループすると自動的に終わるようにしてみます。

import Control.Monad

import Data.Function

main :: IO ()
main = do
putStrLn "Start"
($ 3) . fix $ \loop n -> do -- 初期値に3を与えてループ開始
unless (n == 0) $ do -- nが0じゃ無ければ次を実行
str <- getLine -- 入力待ち
unless (str == "quit") $ do -- "quit"かどうか確認
putStrLn (reverse str) -- 反転して表示
loop (n-1) -- nを1減らして繰り返す
putStrLn "Bye"

$を使ってfixで作った関数に値を適用しています。


fixだよ!!!

無名関数で再帰するjavascript