Особенности реализации первой части в отчёте
This commit is contained in:
@@ -209,15 +209,169 @@
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Математическое описание}
|
||||
% \newpage
|
||||
% \section {Математическое описание}
|
||||
|
||||
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Особенности реализации}
|
||||
\section{Особенности реализации}
|
||||
Согласно заданию для каждой части работы был создан отдельный проект \texttt{stack}.
|
||||
|
||||
\subsection{Часть 1: Синтаксический анализ арифметических выражений}
|
||||
|
||||
\subsubsection{Тип Parser}
|
||||
Тип \texttt{Parser} обеспечивает разбор входной строки по заданным правилам. Он принимает на вход список токенов (например, символов) и пытается разобрать их в соответствии с описанными правилами, возвращая либо результат с оставшейся частью строки, либо \texttt{Nothing}, если разбор не удался.
|
||||
|
||||
Код типа \texttt{Parser} представлен в листинге~\ref{lst:parser_type}.
|
||||
\begin{itemize}
|
||||
\item Вход: список токенов.
|
||||
\item Выход: результат разбора в виде \texttt{Maybe ([tok], a)}.
|
||||
\end{itemize}
|
||||
|
||||
Для типа \texttt{Parser} определены представители классов типов для \texttt{Functor}, \texttt{Applicative} и \texttt{Alternative}. Представитель \texttt{Functor} позволяет применять функцию к результату разбора парсера. Представители \texttt{Applicative} и \texttt{Alternative} позволяют последовательно комбинировать разные парсеры и функции и составлять сложные парсеры из простых.
|
||||
|
||||
\begin{lstlisting}[caption={Определение типа Parser и его представителей для классов типов Functor, Applicative и Alternative.}, label={lst:parser_type}]
|
||||
newtype Parser tok a =
|
||||
Parser { runParser :: [tok] -> Maybe ([tok], a)}
|
||||
|
||||
instance Functor (Parser tok) where
|
||||
fmap g (Parser p) = Parser $ \xs ->
|
||||
case p xs of
|
||||
Nothing -> Nothing
|
||||
Just (cs, c) -> Just (cs, g c)
|
||||
|
||||
instance Applicative (Parser tok) where
|
||||
pure x = Parser $ \toks -> Just (toks, x)
|
||||
Parser u <*> Parser v = Parser $ \xs ->
|
||||
case u xs of
|
||||
Nothing -> Nothing
|
||||
Just (xs', g) ->
|
||||
case v xs' of
|
||||
Nothing -> Nothing
|
||||
Just (xs'', x) -> Just (xs'', g x)
|
||||
|
||||
instance Alternative (Parser tok) where
|
||||
empty = Parser $ \_ -> Nothing
|
||||
Parser u <|> Parser v = Parser $ \xs ->
|
||||
case u xs of
|
||||
Nothing -> v xs
|
||||
z -> z
|
||||
\end{lstlisting}
|
||||
|
||||
\subsubsection{Работа с арифметическими операциями}
|
||||
В листинге~\ref{lst:Operation} представлен код определения класса \texttt{Operation}, а также нескольких вспомогательных функций для работы с ним. Тип используется для хранения одной из четырёх арифметических операций: сложение, вычитание, умножение и деление. Функция \texttt{operationToString} принимает значение типа \texttt{Operation} и возвращает его строковое представление. Функция \texttt{operationToOperator} также принимает значение типа \texttt{Operation}, а возвращает функцию, соответствующую арифметической операции.
|
||||
|
||||
\begin{lstlisting}[caption={Определение типа Operation и функций для работы с ним.}, label={lst:Operation}]
|
||||
data Operation = Add | Sub | Mul | Div deriving Show
|
||||
|
||||
operationToString :: Operation -> String
|
||||
operationToString op = case op of
|
||||
Add -> "+"
|
||||
Sub -> "-"
|
||||
Mul -> "*"
|
||||
Div -> "/"
|
||||
|
||||
operationToOperator :: Operation -> (Int -> Int -> Int)
|
||||
operationToOperator op = case op of
|
||||
Add -> (+)
|
||||
Sub -> (-)
|
||||
Mul -> (*)
|
||||
Div -> div
|
||||
\end{lstlisting}
|
||||
|
||||
\subsubsection{Базовые парсеры}
|
||||
|
||||
В этом разделе рассматриваются основные парсеры, используемые для разбора арифметических выражений. Эти парсеры являются строительными блоками для более сложных выражений. Их код представлен в листинге~\ref{lst:base_parsers}.
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{satisfy} — парсит символ, удовлетворяющий предикату, и возвращает его.
|
||||
\item \texttt{char} — парсит один заданный символ и возвращает его.
|
||||
\item \texttt{digit} — парсит одну цифру и возвращает в виде символа.
|
||||
\item \texttt{skipSpaces} — парсит все пробелы пока не встретить символ, который пробелом не является.
|
||||
\item \texttt{number} — парсит целое число (последовательность цифр) и возвращает в виде~\texttt{Int}.
|
||||
\item \texttt{operation} — парсит арифметическую операцию (\texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}) и возвращает как значение типа \texttt{Operation}.
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}[caption={Базовые парсеры}, label={lst:base_parsers}]
|
||||
satisfy :: (tok -> Bool) -> Parser tok tok
|
||||
satisfy pr = Parser $ \case
|
||||
(c:cs) | pr c -> Just (cs, c)
|
||||
_ -> Nothing
|
||||
|
||||
char :: Char -> Parser Char Char
|
||||
char c = satisfy (== c)
|
||||
|
||||
digit :: Parser Char Char
|
||||
digit = satisfy isDigit
|
||||
|
||||
skipSpaces :: Parser Char String
|
||||
skipSpaces = many (char ' ')
|
||||
|
||||
number :: Parser Char Int
|
||||
number = skipSpaces *> (strToInt <$> some digit)
|
||||
where
|
||||
strToInt = foldl (\acc x -> acc * 10 + digitToInt x) 0
|
||||
|
||||
operation :: Parser Char Operation
|
||||
operation = skipSpaces *> (
|
||||
char '+' *> pure Add <|>
|
||||
char '-' *> pure Sub <|>
|
||||
char '*' *> pure Mul <|>
|
||||
char '/' *> pure Div
|
||||
)
|
||||
\end{lstlisting}
|
||||
|
||||
\subsubsection{Парсер expression}
|
||||
Парсер \texttt{expression}, код которого представлен в листинге~\ref{lst:expression}, парсит выражение вида \texttt{<число> <операция> <число>}. В случае успеха возвращает кортеж вида: \texttt{(Int -- левый операнд, Operation -- операция, Int -- правый операнд)}. Является комбинацей парсеров \texttt{number} и \texttt{operation}. Не чувствителен к пробелам до выражения и внутри него, между операндами и оператором. Поглощает также пробелы после выражения с помощью парсера \texttt{skipSpaces}.
|
||||
|
||||
\begin{lstlisting}[caption={Код функции expression}, label={lst:expression}]
|
||||
expression :: Parser Char (Int, Operation, Int)
|
||||
expression = (,,) <$> number <*> operation <*> number <* skipSpaces
|
||||
\end{lstlisting}
|
||||
|
||||
\subsubsection{Функция processExpression}
|
||||
Код функции \texttt{processExpression} представлен в листинге~\ref{lst:processExpression}.
|
||||
Функция принимает строку, парсит её как выражение, вычисляет результат и возвращает строку с ответом. При ошибке парсинга генерирует ошибку.
|
||||
|
||||
Вход: \texttt{String} — строка с выражением.
|
||||
Выход: \texttt{String} — результат вычисления в формате \texttt{a op b = result}.
|
||||
|
||||
Вспомогательная функция \texttt{calculateExpression} используется для вычисления результата. На вход она получает операнды и операцию, а возвращает вычисленное значение. Её код также представлен в листинге~\ref{lst:processExpression}.
|
||||
|
||||
\begin{lstlisting}[caption={Код функции processExpression}, label={lst:processExpression}]
|
||||
processExpression :: String -> String
|
||||
processExpression s = case runParser expression s of
|
||||
Nothing -> error $ "Не удалось прочитать выражение: \"" ++ s ++ "\""
|
||||
Just (cs, (a, op, b)) -> case cs of
|
||||
[] -> show a ++ " " ++ operationToString op ++ " " ++
|
||||
show b ++ " = " ++ show (calculateExpression (a, op, b)) ++ "\n"
|
||||
_ -> error $ "Не удалось прочитать выражение: \"" ++ s ++ "\""
|
||||
|
||||
|
||||
calculateExpression :: (Int, Operation, Int) -> Int
|
||||
calculateExpression (a, op, b) = (operationToOperator op) a b
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsubsection{Функция main}
|
||||
Код функции \texttt{main} представлен в листинге~\ref{lst:main}.
|
||||
Функция \texttt{main} считывает имя файла у пользователя, читает файл, построчно обрабатывает каждое выражение с помощью \texttt{processExpression} и выводит результат.
|
||||
|
||||
\begin{lstlisting}[caption={Код функции main}, label={lst:main}]
|
||||
main :: IO ()
|
||||
main =
|
||||
putStrLn "Введите имя файла:" >>
|
||||
getLine >>= \fileName ->
|
||||
readFile fileName >>= \content ->
|
||||
let expressions = lines content in
|
||||
putStrLn $ concatMap processExpression expressions
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsection{Часть 2: Синтаксический анализ текста и генерация фраз}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user