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