Часть от второй части

This commit is contained in:
2024-12-09 22:41:26 +03:00
parent 5caf07c35c
commit a497908ed0

View File

@@ -371,8 +371,112 @@ calculateExpression (a, op, b) = (operationToOperator op) a b
\subsection{Часть 2: Синтаксический анализ текста и генерация фраз} \subsection{Часть 2: Синтаксический анализ текста и генерация фраз}
\subsubsection{Функция splitText}
На первом этапе необходимо разделить текст на предложения и слова. Предложения определяются с помощью разделителей \texttt{.!?;:()}. В словах удаляются небуквенные символы и цифры. Код функции \texttt{splitText}, ответственной за разбиение текста и очистку слов, представлен в листинге~\ref{lst:splitText}. Функция принимает на вход строку -- исходный текст, а возвращает список предложений, где каждое предложение представлено в виде списка слов.
\begin{lstlisting}[caption={Функция splitText для разбора текста на предложения и слова}, label={lst:splitText}]
splitText :: String -> [[String]]
splitText text = filter (not . null) $ map (processSentence . words) (splitSentences text)
where
splitSentences :: String -> [String]
splitSentences [] = []
splitSentences s =
let (sentence, rest) = break isSeparator s
rest' = dropWhile isSeparator rest
in if null sentence
then splitSentences rest'
else sentence : splitSentences rest'
isSeparator :: Char -> Bool
isSeparator c = c `elem` ".!?;:()"
processSentence :: [String] -> [String]
processSentence = filter (not . null) . map cleanWord
cleanWord :: String -> String
cleanWord = map toLower . filter isLetter
\end{lstlisting}
\subsubsection{Функция buildDictionary}
На основе полученных предложений строится словарь, где ключами являются либо отдельные слова, либо пары слов, а значениями — списки возможных продолжений (следующее слово или пара слов для триграмм). Для этого используются биграммы и триграммы. Код функции \texttt{buildDictionary}, формирующей словарь представлен в листинге~\ref{lst:buildDictionary}.
\begin{lstlisting}[caption={Функция buildDictionary для формирования словаря N-грамм}, label={lst:buildDictionary}]
buildDictionary :: [[String]] -> Map String [String]
buildDictionary sentences =
let bigrams = [ (w1, w2) | s <- sentences, (w1:w2:_) <- tails s ]
trigrams = [ (w1, w2, w3) | s <- sentences, (w1:w2:w3:_) <- tails s ]
singleKeys = foldr (\(w1, w2) acc -> Map.insertWith (++) w1 [w2] acc) Map.empty bigrams
singleKeys' = foldr (\(w1, w2, w3) acc -> Map.insertWith (++) w1 [w2 ++ " " ++ w3] acc) singleKeys trigrams
doubleKeys = foldr (\(w1, w2, w3) acc -> Map.insertWith (++) (w1 ++ " " ++ w2) [w3] acc) Map.empty trigrams
combined = Map.unionWith (++) singleKeys' doubleKeys
in Map.map nub combined
\end{lstlisting}
\subsubsection{Функция generatePhrase}
Программа случайным образом формирует фразу длиной от 2 до 15 слов, используя словарь. На каждом шаге выбирается случайное продолжение, пока не будут исчерпаны возможные варианты или не достигнута заданная длина. Код функции для генерации фразы приведён в листинге~\ref{lst:generatePhrase}.
\begin{lstlisting}[caption={Функция generatePhrase для генерации фразы}, label={lst:generatePhrase}]
generatePhrase :: Map String [String] -> String -> StdGen -> [String]
generatePhrase dict start initGenState =
let (len, initGenState') = randomR (2,15 :: Int) initGenState
in reverse $ gp start [] len initGenState'
where
gp :: String -> [String] -> Int -> StdGen -> [String]
gp key acc n genState
| n <= 0 = acc
| otherwise =
case Map.lookup key dict of
Nothing -> acc
Just [] -> acc
Just vals ->
let (i, newGenState) = randomR (0, length vals - 1) genState
next = vals !! i
in gp next (next:acc) (n - length (words next)) newGenState
\end{lstlisting}
Функция \texttt{processInput} (листинг~\ref{lst:processInput}) проверяет, существует ли введённое пользователем слово(или пара слов) в словаре, и если да — генерирует фразу.
\begin{lstlisting}[caption={Функция processInput для обработки пользовательского ввода}, label={lst:processInput}]
processInput :: Map String [String] -> String -> IO ()
processInput dict input =
if Map.member input dict then
newStdGen >>= \gen ->
putStrLn $ unwords $ generatePhrase dict input gen
else
putStrLn "Нет в словаре"
\end{lstlisting}
\subsubsection{Функция twoModelsDialog}
Реализован режим, в котором две модели N-грамм, построенные на разных текстах, обмениваются сообщениями. Начальное слово или пару слов задаёт пользователь, затем модели по очереди генерируют ответы, основываясь на словах, из которых состоит последнее сообщение их собеседника. Код, реализующий диалог, представлен в листинге~\ref{lst:twoModelsDialog}.
\begin{lstlisting}[caption={Функция twoModelsDialog для организации диалога между двумя моделями}, label={lst:twoModelsDialog}]
twoModelsDialog :: Map String [String] -> Map String [String] -> String -> Int -> IO ()
twoModelsDialog dict1 dict2 start m =
newStdGen >>= \gen ->
let first = generatePhrase dict1 start gen
in putStrLn ("Модель 1: (" ++ start ++ ") " ++ unwords first) >>
loop dict1 dict2 first m
where
loop :: Map String [String] -> Map String [String] -> [String] -> Int -> IO ()
loop _ _ _ 0 = return ()
loop d1 d2 prev i =
putStr "Модель 2: " >>
dialogStep d2 prev >>= \resp ->
if null resp then return () else
putStr "Модель 1: " >>
dialogStep d1 resp >>= \resp2 ->
if null resp2 then return () else
loop d1 d2 resp2 (i-1)
\end{lstlisting}
Данный подход позволяет динамически генерировать фразы и организовывать имитацию диалога между двумя текстовыми моделями, что служит демонстрацией возможностей построенной системы N-грамм.
\newpage \newpage