「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コメント:
コメントを投稿