\setlist[enumerate,itemize]{leftmargin=1.2cm}%отступ в перечислениях
\hypersetup{colorlinks,
allcolors=[RGB]{010 090 200}}%красивые гиперссылки (не красные)
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
\lstloadlanguages{ Haskell}
% включаем кириллицу и добавляем кое−какие опции
\lstset{tabsize=2,
breaklines,
basicstyle=\footnotesize,
columns=fullflexible,
flexiblecolumns,
numbers=left,
numberstyle={\footnotesize},
keywordstyle=\color{blue},
inputencoding=cp1251,
extendedchars=true
}
\lstdefinelanguage{MyC}{
language=Haskell,
% ndkeywordstyle=\color{darkgray}\bfseries,
% identifierstyle=\color{black},
% morecomment=[n]{/**}{*/},
% commentstyle=\color{blue}\ttfamily,
% stringstyle=\color{red}\ttfamily,
% morestring=[b]",
% showstringspaces=false,
% morecomment=[l][\color{gray}]{//},
keepspaces=true,
escapechar=\%,
texcl=true
}
\textheight=24cm % высота текста
\textwidth=16cm % ширина текста
\oddsidemargin=0pt % отступ от левого края
\topmargin=-1.5cm % отступ от верхнего края
\parindent=24pt % абзацный отступ
\parskip=5pt % интервал между абзацами
\tolerance=2000 % терпимость к "жидким" строкам
\flushbottom% выравнивание высоты страниц
% Настройка листингов
\lstset{
language=Haskell,
extendedchars=\true,
inputencoding=utf8,
keepspaces=true,
captionpos=t,
}
\begin{document}% начало документа
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
\begin{center}
\hfill\break
\hfill\break
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
\hfill\break
\hfill\break
\hfill\break
\hfill\break
\large{Отчет по курсовой работе}\\
\large{по дисциплине}\\
\large{<<Функциональное программирование>>}\\
\large{Вариант 5}\\
\hfill\break
% \hfill \break
% \hfill \break
\end{center}
\small{
\begin{tabular}{lrrl}
\!\!\!Студент, &\hspace{2cm}&&\\
\!\!\!группы 5130201/20102 &\hspace{2cm}&\underline{\hspace{3cm}}&Тищенко А. А. \\\\
\!\!\!Преподаватель,\\\hspace{-5pt}к. т. н., доц. &\hspace{2cm}&\underline{\hspace{3cm}}& Моторин Д. Е. \\\\
\item Вычислять результат выражения и выводить его на экран.
\end{itemize}
\item Разработка синтаксического анализатора текста и генератора продолжения текста. Задачи:
\begin{enumerate}
\item Считать текст из файла, название которого вводит пользователь, и выполнить его синтаксический анализ:
\begin{itemize}
\item Разбить текст на предложения, используя следующие правила: слова состоят только из букв; предложения состоят из слов и разделяются символами: \texttt{. ! ? ; : ( )}.
\item Удалить из текста все символы пунктуации и цифры.
\end{itemize}
\item Построить модель N-грамм:
\begin{itemize}
\item Использовать биграммы и триграммы.
\item Составить словарь, где ключами являются одно слово или пара слов, а значениями — списки всех уникальных возможных продолжений.
\item При вводе одного или пары слов возвращать случайную строку длиной от 2 до 15 слов, основанную на созданном словаре.
\item Если введенное слово отсутствует в ключах словаря, выводить сообщение об этом.
\end{itemize}
\item Организовать диалог между двумя моделями N-грамм, созданными на основе двух различных текстов:
\begin{itemize}
\item Пользователь задает начальное слово или пару слов и количество сообщений (глубину диалога).
\item Ответ каждой модели основывается на последнем слове (или предпоследнем, если последнее отсутствует в словаре) из предыдущего сообщения оппонента.
\end{itemize}
\end{enumerate}
В качестве текстов для построения моделей использовать произведения Антона Павловича Чехова.
\end{enumerate}
% \newpage
% \section {Математическое описание}
\newpage
\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}.
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}]
\subsection{Часть 2: Синтаксический анализ текста и генерация фраз}
\subsubsection{Функция splitText}
На первом этапе необходимо разделить текст на предложения и слова. Предложения определяются с помощью разделителей \texttt{.!?;:()}. В словах удаляются небуквенные символы и цифры. Код функции \texttt{splitText}, ответственной за разбиение текста и очистку слов, представлен в листинге~\ref{lst:splitText}. Функция принимает на вход строку -- исходный текст, а возвращает список предложений, где каждое предложение представлено в виде списка слов.
\begin{lstlisting}[caption={Функция splitText для разбора текста на предложения и слова}, label={lst:splitText}]
На основе полученных предложений строится словарь, где ключами являются либо отдельные слова, либо пары слов, а значениями — списки возможных продолжений (следующее слово или пара слов для триграмм). Для этого используются биграммы и триграммы. Код функции \texttt{buildDictionary}, формирующей словарь представлен в листинге~\ref{lst:buildDictionary}.
\begin{lstlisting}[caption={Функция buildDictionary для формирования словаря N-грамм}, label={lst:buildDictionary}]
Программа случайным образом формирует фразу длиной от 2 до 15 слов, используя словарь. На каждом шаге выбирается случайное продолжение, пока не будут исчерпаны возможные варианты или не достигнута заданная длина. Код функции для генерации фразы приведён в листинге~\ref{lst:generatePhrase}.
\begin{lstlisting}[caption={Функция generatePhrase для генерации фразы}, label={lst:generatePhrase}]
in gp next (next:acc) (n - length (words next)) newGenState
\end{lstlisting}
Функция \texttt{processInput} (листинг~\ref{lst:processInput}) проверяет, существует ли введённое пользователем слово(или пара слов) в словаре, и если да — генерирует фразу.
\begin{lstlisting}[caption={Функция processInput для обработки пользовательского ввода}, label={lst:processInput}]
putStrLn $ unwords $ generatePhrase dict input gen
else
putStrLn "Нет в словаре"
\end{lstlisting}
\subsubsection{Функция twoModelsDialog}
Реализован режим, в котором две модели N-грамм, построенные на разных текстах, обмениваются сообщениями. Начальное слово или пару слов задаёт пользователь, затем модели по очереди генерируют ответы, основываясь на словах, из которых состоит последнее сообщение их собеседника. Код, реализующий диалог, представлен в листинге~\ref{lst:twoModelsDialog}.
\begin{lstlisting}[caption={Функция twoModelsDialog для организации диалога между двумя моделями}, label={lst:twoModelsDialog}]
Данный подход позволяет динамически генерировать фразы и организовывать имитацию диалога между двумя текстовыми моделями, что служит демонстрацией возможностей построенной системы N-грамм.
\newpage
\section{Результаты работы программы}
\newpage
\section*{Заключение}
\addcontentsline{toc}{section}{Заключение}
\newpage
\section*{Список литературы}
\addcontentsline{toc}{section}{Список литературы}
\vspace{-1.5cm}
\begin{thebibliography}{0}
\bibitem{JuicyPixels}
Hackage -- unescaping-print: Tiny package providing unescaping versions of show and print, URL: \url{https://hackage.haskell.org/package/unescaping-print}, Дата обращения: 09.12.2024.
\end{thebibliography}
\end{document}
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.