第13回Ruby勉強会@関西の演習課題をHaskellで解いてみました
問題はこちらです。今回の課題はオブジェクト変数、クラス変数の使い分けがポイントです。Haskellでこういう状態を覚えておくようなプログラムは書きにくいです。悩んだ末、結局IORefを使ってしまいました。うーむ。
module Main (main) where import Data.Char (toUpper, isUpper) import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef) import Data.List (intersperse, isPrefixOf) import System.IO (hFlush, stdout) import System.Random (randomRIO) join :: [a] -> [[a]] -> [a] join = (concat .) . intersperse type TalkS = String -> IO (Maybe String) newGrannyFactory :: IO (String -> Bool -> IO (String, TalkS)) newGrannyFactory = return . newGranny =<< newIORef [] newGranny :: IORef [String] -> String -> Bool -> IO (String, TalkS) newGranny nameHistory name hearingAid = do gr <- greeting modifyIORef nameHistory (\ a -> name : a) byeCount <- newIORef 0 return $ (gr, talkWithGranny byeCount hearingAid) where greeting = do names <- readIORef nameHistory let names' = map (++ " san") names if (null names') then return "Konnitiha." else if (length names' < 4) then return $ join " to " names' ++ " ga hanashiteita ko dane." else return $ head names' ++ " tachi ga hanashiteita ko dane." talkWithGranny :: IORef Int -> Bool -> TalkS talkWithGranny byeCount hearingAid msg | hearingAid && isByeWithHearingAid = return Nothing | not hearingAid && isByeWithoutHearingAid = do count <- readIORef byeCount case count of 2 -> return Nothing _ -> modifyIORef byeCount (+ 1) >> replayLoud | isLoud = writeIORef byeCount 0 >> replayYear | otherwise = writeIORef byeCount 0 >> replayLoud where isByeWithHearingAid = "BYE" `isPrefixOf` map toUpper msg isByeWithoutHearingAid = "BYE" == msg isLoud = (not $ null msg) && (all isUpper msg) replayYear = do n <- randomRIO (0, 20) return $ Just $ "Iya-, " ++ (show $ 1930 + n) ++ "nen irai naine-!!" replayLoud = do return $ Just $ "Ha?! motto ookina koe de hanashiteokure, bouya!!" main :: IO () main = do newGranny <- newGrannyFactory newGranny "Tome" True >>= (\ (gr, tk) -> putStrLn gr >> loop tk) newGranny "Ume" False >>= (\ (gr, tk) -> putStrLn gr >> loop tk) newGranny "Kathy" False >>= (\ (gr, tk) -> putStrLn gr >> loop tk) newGranny "Sakura" False >>= (\ (gr, tk) -> putStrLn gr >> loop tk) newGranny "Kiku" False >>= (\ (gr, tk) -> putStrLn gr >> loop tk) where loop talkWithGranny = do repley <- talkWithGranny =<< getMessage case repley of Just msg -> putStrLn msg >> loop talkWithGranny Nothing -> return () getMessage = putStr "> " >> hFlush stdout >> getLine
実行例:
$ ./granny.exe Konnitiha. > aaa Ha?! motto ookina koe de hanashiteokure, bouya!! > AAA Iya-, 1950nen irai naine-!! > bye!! Tome san ga hanashiteita ko dane. > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE Ume san to Tome san ga hanashiteita ko dane. > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE Kathy san to Ume san to Tome san ga hanashiteita ko dane. > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE Sakura san tachi ga hanashiteita ko dane. > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE Ha?! motto ookina koe de hanashiteokure, bouya!! > BYE