「圏論やモナドが、どうして文書処理やXMLと関係するのですか?」を読んでるところ
http://d.hatena.ne.jp/m-hiyama/20070125/1169702291
エントリを読んで、演習課題を自分なりにやってみました。ひとしきり読んだんだけど、まだ分からないところがたくさんあるのできっともっと読みます。
課題8でMonadにするところで小一時間ほど悩む。多分、型の使い方が今イチ分かってないんだと思います。
{- ProcessTemplate.hs http://d.hatena.ne.jp/m-hiyama/20070125 nisikawa 2007.03.15 -} import Data.Maybe import Text.ParserCombinators.Parsec -- 課題1 isWellFormed :: String -> Bool isWellFormed "" = True isWellFormed s = iter 0 0 s where iter l r [] = l == r iter l r (x:xs) | x == '{' = iter (l + 1) r xs | x == '}' = if l > r then iter l (r + 1) xs else False | x == '\\' = if null xs then iter l r [] else iter l r (tail xs) | otherwise = iter l r xs -- 課題2 removeParens :: String -> String removeParens "" = "" removeParens (x:xs) | isParens x = removeParens xs | x == '\\' = if null xs then [x] else x:y:removeParens (tail xs) | otherwise = x:removeParens xs where y = head xs isParens c = c `elem` "{}" -- 課題3(以下エスケープは無視,メンドーだから) data Value a = Text a | Block a [Value a] deriving Show run line = case runParser parseTemplate "" [] line of Left e -> error $ show e Right x -> x parseTemplate = do x <- (try block) <|> text xs <- (try parseTemplate) <|> return [] return (x:xs) block = do char '{' b <- (try (many1 (noneOf "{}"))) <|> return "" bs <- (try parseTemplate) <|> return [] char '}' return $ Block b bs text = do t <- many1 $ noneOf "{}" return $ Text t type Template = String type Context = [(String,String)] processTemplate :: Template -> Context -> Template processTemplate template ctx = let vals = run template in f vals ctx where f [] _ = "" f ((Text t):xs) ctx = t ++ f xs ctx f ((Block b bs):xs) ctx = let ms = lookup b ctx in case ms of Nothing -> blocked (b ++ f bs []) ++ f xs ctx Just s -> s ++ f bs [] ++ f xs ctx blocked b = "{" ++ b ++ "}" processContext :: Context -> Context -> Context processContext c ctx | null ctx = c | otherwise = map (\(x,y) -> (x, processTemplate y ctx)) c data T = T Template | C Context deriving Show process :: T -> T -> T process x (T _) = x process (T t) (C ctx) = T $ processTemplate t ctx process (C c) (C ctx) = C $ processContext c ctx -- 課題4 notNestedBlocks template = f $ run template where f [] = [] f ((Text t):xs) = f xs f ((Block b []):xs) = b:f xs f ((Block b bs):xs) = f bs ++ f xs -- 課題5 removeNestedBlockParens template = f $ run template where f [] = [] f ((Text t):xs) = t ++ f xs f ((Block b []):xs) = blocked b ++ f xs f ((Block b bs):xs) = b ++ f bs ++ f xs -- 課題6 nestLevel = nestLevel' . run nestLevel' :: [Value a] -> Int nestLevel' [] = 0 nestLevel' vals = maximum $ map f vals where f (Text t) = 0 f (Block b bs) = 1 + nestLevel' bs -- 課題7 data Templ a = Templ a deriving (Show, Eq) processTemplate' :: Templ String -> (String -> Templ String) -> Templ String processTemplate' (Templ t) fun = let vals = run t in f vals fun where f [] _ = Templ [] f ((Text t):xs) fun = let (Templ s) = f xs fun in Templ $ t ++ s f ((Block b bs):xs) fun = let (Templ s1) = fun b (Templ s2) = f bs unit (Templ s3) = f xs fun in Templ $ s1 ++ s2 ++ s3 -- "{ho{gefu}ga}"みたいのをうまく展開できない...Templはネストしないからいいのかな。 con :: Context -> String -> Templ String con ctx b = Templ $ fromMaybe (blocked b) (lookup b ctx) unit :: String -> Templ String unit = Templ . blocked ext :: (String -> Templ String) -> Templ String -> Templ String ext f = flip processTemplate' f -- 課題8 con1 = con [("hoge","111"),("fuga","222"),("piyo","333")] con2 = con [("111","AAA"),("222","BBB"),("333","CCC")] t = Templ "hoge{fuga}{piyo}" f1 = (ext unit) (Templ "hoge") == Templ "hoge" f2 = (ext con1) (unit "hoge") == Templ "111" f3 = (ext((ext con2) . con1)) t == (ext con2 . ext con1) t