「Haskell で変数の更新を伴うインデックス付きループを再帰で置き換えるには」のつづき
1. Data.IORef で変数の更新
Python のような言語では、変数の更新はごく普通に書ける。
x = 100 print x #=> 100 x = 200 print x #=> 200 x = 200 * 2 print x #=> 400
Haskell では Data.IORef を使うと、一見、変数の更新をしているかのような記述ができる。
このモジュールの機能は、
Mutable references in the IO monad.
使う関数は以下の 4 つ。返される値が IO で包まれている。
- newIORef :: a -> IO (IORef a)
- readIORef :: IORef a -> IO a
- writeIORef :: IORef a -> a -> IO ()
- modifyIORef :: IORef a -> (a -> a) -> IO ()
それぞれ関数名が示しているように、IORef 型の値を生成し、読み取り、書き出し、変更する。
import Data.IORef main = newIORef 100 >>= \x -> readIORef x >>= print >> -- 100 writeIORef x 200 >> readIORef x >>= print >> -- 200 modifyIORef x (*2) >> readIORef x >>= print -- 400
do 式を使うなら、
main = do x <- newIORef 100 print =<< readIORef x -- 100 writeIORef x 200 print =<< readIORef x -- 200 modifyIORef x (*2) print =<< readIORef x -- 400
2. 変数を更新する例
「文字列の配列の中から最長の文字列と、そのインデックスを返す」関数を Data.IORef を使って書くなら、
import Data.IORef longestWord :: [String] -> IO (String, Int) longestWord (x:xs) = newIORef x >>= \word -> newIORef 0 >>= \idx -> loop word idx 1 xs where loop word idx i [] = readIORef word >>= \word' -> readIORef idx >>= \idx' -> return (word', idx') loop word idx i (x:xs) = readIORef word >>= \word' -> (if length word' < length x then writeIORef word x >> writeIORef idx i else return ()) >> loop word idx (i+1) xs main = (longestWord $ words "The quick brown fox") >>= print
うーん、読みやすくはないなぁ。。 (@_@;
do 式で置き換えるなら、
longestWord :: [String] -> IO (String, Int) longestWord (x:xs) = do word <- newIORef x idx <- newIORef 0 loop word idx 1 xs where loop word idx i [] = do word' <- readIORef word idx' <- readIORef idx return (word', idx') loop word idx i (x:xs) = do word' <- readIORef word if length word' < length x then do writeIORef word x writeIORef idx i else return () loop word idx (i+1) xs
ループのインデックスに相当する変数も IORef 型の値にするなら、
longestWord :: [String] -> IO (String, Int) longestWord (x:xs) = do word <- newIORef x idx <- newIORef 0 i <- newIORef 1 loop word idx i xs where loop word idx i [] = do word' <- readIORef word idx' <- readIORef idx return (word', idx') loop word idx i (x:xs) = do word' <- readIORef word i' <- readIORef i if length word' < length x then do writeIORef word x writeIORef idx i' else return () modifyIORef i (+1) loop word idx i xs
0コメント:
コメントを投稿