Problem K

Nov 15, 2007 11:18

условие
решение на хаскелл
другое решение на хаскелл

Смотрел, смотрел и не удержался... Привожу своё решение.
Оно может быть интересно как пример фатального влияния ооп на человеческий мозг.
Коротко о решении:
модуль Sheet (использует Expression)
Sheet a - тип данных таблица - по сути набор именованных значений а. Определены операция вычисления (Sheet Expr -> Sheet CExpr) и преобразования в текстовый формат (Sheet CExpr -> String). Здесь же анализ циклическиз зависимостей между выражениями в таблице.
модуль Expression
CExpr - тип данных константа, описывает базовые типов.
Function - тип данных функция, представляет операции над базовыми типами.
Expr - тип данных выражение, служит для представления произвольного выражения.
Определены операция вычисления выражения ((String->CExpr) -> Expr -> CExpr) и взятие списка переменных (Expr -> [String])
В модулях SheetParser и ExprParser инкапсулировано знание о входном формате, строковом представление таблицы Sheet Expr и выражения Expr.
модуль Main - решает поставленную задачу.
Зависимости:
Main -> (Sheet -> Expr) -- 1 здесь сосредоточена вся бизнес логика (вычисления) -> (SheetParser -> Sheet -> (ExprParser -> Expr) -> Expr) -- 2 используются только конструкторы Sheet и Expr
Где бы найти строгого судью, который решил бы какое из решений лучше...
Пример работы:
7 4 12 =C2 3 'Sample =A1+B1*C1/5 =A2*B1 =B3-C3 ='Spread 'Test =4-3 5 ='Sheet =A9 =1/0 =A5 =B5 =1+C6+1 =5A =A1++A1 =1+ x =A5 =A6+B6 =A1 =A3 =A4 =A5 12 -4 3 'Sample 4 -16 -4 'Spread 'Test 1 5 'Sheet #NoRef #div by 0 #Cycle #Cycle #Cycle #Parsing #Parsing #Parsing #Parsing #Cycle #Parsing 12 'Test #Cycle Код:
----------------------------------------------------------------------------------------- module Main where import Sheet import SheetParser main = do input <- getContents let sheet = mkExprSheet $ mkStringSheet $ parseInput input putStrLn $ showSheet $ evaluate sheet ----------------------------------------------------------------------------------------- module Sheet where import Expression import Data.Graph import Data.List data Sheet a = Sheet [(String, a)] deriving Show mapS :: (a -> b) -> (Sheet a) -> (Sheet b) mapS f (Sheet cs) = Sheet $ map (\(k, v) -> (k, f v)) cs evaluate :: Sheet Expr -> Sheet CExpr evaluate s@(Sheet cells) = Sheet values where values = map eval cells resolve var = resolve' $ lookup var values resolve' Nothing = CError "NoRef" resolve' (Just e) = e cyclic = cyclicCells s eval (var, expr) | var `elem` cyclic = (var, CError "Cycle") | otherwise = (var, evaluateExpr resolve expr) -- get names of cyclic referenced variables cyclicCells :: Sheet Expr -> [String] cyclicCells (Sheet cells) = concatMap flattenSCC $ filter isCyclic sccs where sccs = stronglyConnComp $ map describeRefs cells describeRefs (var, expr) = (var, var, vars expr) isCyclic (AcyclicSCC _) = False isCyclic (CyclicSCC _) = True -- get display string showSheet :: Sheet CExpr -> String showSheet exprSheet = unlines $ map (tail.concatMap ('\t':)) $ map (snd.unzip) $ groupBy' (tail.fst) cells where (Sheet cells) = mapS showCell exprSheet groupBy' f = groupBy (\a b -> f a == f b) showCell :: CExpr -> String showCell (CText t) = '\'':t showCell (CNumber n) = show n showCell (CError e) = '#':e showCell (CEmpty) = [] ----------------------------------------------------------------------------------------- module Expression where import Data.Char --------------------------------- -- | Expression data CExpr = CNumber Integer | CText String | CError String | CEmpty deriving (Show) data Expr = EVar String -- variable | EConst CExpr -- const | EFunc Function [Expr] -- function deriving (Show) data Function = Infix String -- name (CExpr -> CExpr -> CExpr) -- evaluator fname :: Function -> String fname (Infix n _) = n instance Show Function where show = fname --- resolve variables in expression vars :: Expr -> [String] vars (EVar var) = [var] vars (EFunc _ exprs) = concat $ map vars exprs vars _ = [] --------------------------------------------- -- | Evaluation type ResolveVar = String -> CExpr evaluateExpr :: ResolveVar -> Expr -> CExpr evaluateExpr _ (EConst value) = value evaluateExpr resolve (EVar var) = resolve var evaluateExpr resolve (EFunc f es) = eval f (map (evaluateExpr resolve) es) where eval (Infix _ f) (x:y:[]) = f x y eval _ _ = CError "eval" --------------------------------------------- -- | Supported functions -- Add fadd :: CExpr -> CExpr -> CExpr fadd (CNumber n1) (CNumber n2) = CNumber $ n1 + n2 fadd e@(CError _) _ = e fadd _ e@(CError _) = e fadd _ _ = CError "add" -- Sub fsub :: CExpr -> CExpr -> CExpr fsub (CNumber n1) (CNumber n2) = CNumber $ n1 - n2 fsub e@(CError _) _ = e fsub _ e@(CError _) = e fsub _ _ = CError "sub" -- Mult fmult :: CExpr -> CExpr -> CExpr fmult (CNumber n1) (CNumber n2) = CNumber $ n1 * n2 fmult e@(CError _) _ = e fmult _ e@(CError _) = e fmult _ _ = CError "mult" -- Div fdiv :: CExpr -> CExpr -> CExpr fdiv (CNumber n1) (CNumber n2) | n2 /= 0 = CNumber $ n1 `div` n2 | otherwise = CError "div by 0" fdiv e@(CError _) _ = e fdiv _ e@(CError _) = e fdiv _ _ = CError "div" ----------------------------------------------------------------------------------------- module SheetParser where import Sheet import Expression import ExprParser split :: Eq a => a -> [a] -> [[a]] split _ [] = [] split d xs = split' $ span (d/=) xs where split' (group, []) = [group] split' (group, (r:[])) = group : [[]] split' (group, (r:rs)) = group : split d rs -- parse input, fail if incorrect input parseInput :: String -> (Int, Int, [[String]]) parseInput input | isValid = (rows, columns, rs) where ((r:c:[]):rs) = map (split '\t') $ lines input (rows, columns) = (read r, read c) isValid = (length rs) == rows && all ((columns==).length) rs -- build string sheet mkStringSheet :: (Int, Int, [[String]]) -> Sheet String mkStringSheet (_, _, rs) = Sheet $ concatMap merge $ zip [1..] $ map (zip ['A'..]) rs where merge :: (Int, [(Char, String)]) -> [(String, String)] merge (r, xs) = map (\(c, t) -> (c:(show r), t)) xs -- build expr sheet from string sheet mkExprSheet :: Sheet String -> Sheet Expr mkExprSheet = mapS parseCell where parseCell ('=':rs) = parseExpr functions rs parseCell rs = EConst $ parseCExpr rs functions = [Infix "+" fadd, Infix "-" fsub, Infix "*" fmult, Infix "/" fdiv] ----------------------------------------------------------------------------------------- module ExprParser where import Expression import Data.Char ----------------------------------- -- | Lexer data Term = TNumber Integer | TText String | TRef String | TFunc String | TError String | TEqual deriving Show parseTerm :: [String] -> String -> [Term] parseTerm funcs text = parseTerm' parsers text where parseTerm' :: [String -> (Maybe Term, String)] -> String -> [Term] parseTerm' _ [] = [] parseTerm' [] text = [TError text] parseTerm' (p:ps) text = case p text of (Nothing, _) -> parseTerm' ps text (Just t, rs) -> t : (parseTerm' parsers rs) -- possible terms parsers = [parseEqual, parseNumber, parseText, parseRef, parseFunc funcs] -- '=' parseEqual ('=':rs) = (Just TEqual, rs) parseEqual text = (Nothing, text) -- [0-9]* parseNumber text = case span isDigit text of ([], _) -> (Nothing, text) (digits, rest) -> (Just $ TNumber $ read digits, rest) -- [']:(.*) parseText ('\'':text) = (Just $ TText text, []) parseText text = (Nothing, text) -- [A-Za-z][0-9] parseRef (x:y:rest) | (isLetter x) && (isDigit y) = (Just $ TRef (x:y:[]), rest) parseRef text = (Nothing, text) -- f1|f2|... parseFunc (s:ss) text | s == take (length s) text = (Just $ TFunc s, drop (length s) text) | otherwise = parseFunc ss text parseFunc [] text = (Nothing, text) -------------------------------------------- -- | Parser -- parse expression parseExpr :: [Function] -> String -> Expr parseExpr _ [] = EConst $ CEmpty parseExpr fs text = parseExpr' Nothing terms where terms = parseTerm (map fname fs) text findFunc s = head $ filter ((s==).fname) fs parseExpr' :: (Maybe Expr) -> [Term] -> Expr parseExpr' Nothing ((TNumber n):ts) = parseExpr' (Just $ EConst $ CNumber n) ts parseExpr' Nothing ((TText t):ts) = parseExpr' (Just $ EConst $ CText t) ts parseExpr' Nothing ((TError e):ts) = parseExpr' (Just $ EConst $ CError "Syntax") ts parseExpr' Nothing ((TRef r):ts) = parseExpr' (Just $ EVar r) ts parseExpr' (Just e) ((TFunc f):t:ts) = parseExpr' (Just $ EFunc (findFunc f) (e:(parseExpr' Nothing [t]):[])) ts parseExpr' (Just e) [] = e parseExpr' _ _ = EConst $ CError "Parsing" -- parse constant expression parseCExpr :: String -> CExpr parseCExpr text = parseCExpr' terms where terms = parseTerm [] text parseCExpr' [] = CEmpty parseCExpr' ((TNumber n):[]) = CNumber n parseCExpr' ((TText t):[]) = CText t parseCExpr' _ = CError "Parsing"
Next post
Up