;;; Глобальные константы
(defconstant +glasn+ '(#\А #\Е #\Ё #\И #\О #\У #\Ы #\Э #\Ю #\Я #\а #\е #\ё #\и #\о #\у #\ы #\э #\ю #\я)
"Список гласных букв")
(defconstant +sonorn+ '(#\Л #\М #\Н #\Р #\л #\м #\н #\р #\Й #\й)
"Список сонорных согласных букв")
;;; Функция для проверки, является ли символ гласной буквой
(defun glasn (char)
(member char +glasn+ :test #'char-equal))
;;; Функция для проверки, является ли символ сонорным согласным
(defun sonorn (char)
(member char +sonorn+ :test #'char-equal))
;;; Функция для проверки, является ли символ шумным согласным
(defun shumn (char)
(and (not (glasn char)) (not (sonorn char))))
;;; Функция для разбиения слова на слоги (с учетом правил стечения согласных)
(defun razbit-na-slogi (word)
(let ((slogi nil) ; Список слогов (будет содержать списки символов)
(current-slog nil)) ; Текущий слог (список символов)
(dotimes (i (length word)) ; Перебираем символы в слове
(let ((char (char word i))) ; Получаем текущий символ
(push char current-slog) ; Добавляем символ в текущий слог
(when (glasn char) ; Если текущий символ - гласная
(cond
((= (length current-slog) 1) ; Если слог состоит только из гласной
(push (reverse current-slog) slogi) ; Добавляем слог в список слогов
(setf current-slog nil)) ; Начинаем новый слог
(t ; Если перед гласной есть согласные
(let ((rev-slog (reverse current-slog))) ; Переворачиваем слог для удобства анализа
(cond
((and (>= (length current-slog) 2) ; Если в слоге минимум 2 символа
(shumn (nth 1 rev-slog)) ; И предпоследний - шумный
(shumn (nth 0 rev-slog))) ; И последний (перед гласной) - шумный
; Два шумных согласных: оба отходят к следующему слогу
(push (reverse (subseq rev-slog 1)) slogi) ; Все, кроме последней буквы (гласной) образуют новый слог
(setf current-slog (list char))) ; Начинаем с гласной новый слог
((and (>= (length current-slog) 2) ; Если в слоге минимум 2 символа
(sonorn (nth 1 rev-slog)) ; И предпоследний - сонорный
(sonorn (nth 0 rev-slog)) ; И последний (перед гласной) - сонорный
(not (char-equal (nth 0 rev-slog) #\й))) ; И это не "й"
; Два сонорных согласных (кроме "й"): оба отходят к следующему слогу
(push (reverse (subseq rev-slog 1)) slogi) ; Новый слог без последней буквы(гласной)
(setf current-slog (list char))) ; Начинаем новый слог с гласной
((and (>= (length current-slog) 2) ; Если в слоге минимум 2 символа
(char-equal (nth 1 rev-slog) #\й)) ; Если предпоследний символ - "й"
; "й" всегда отходит к предыдущей гласной (в этом коде - к текущему слогу)
(push (reverse (subseq rev-slog 1)) slogi) ; Новый слог без гласной
(setf current-slog (list char))) ; Начинаем новый слог с гласной
((and (>= (length current-slog) 2) ; Если в слоге минимум 2 символа
(sonorn (nth 1 rev-slog)) ; Если предпоследний символ — сонорный
(shumn (nth 0 rev-slog))) ; Если последний символ — шумный
; Сонорный перед шумным: отходит к предыдущему слогу.
; (Можно добавить свои правила, например, смотреть на соседние буквы в словаре)
(push (reverse (subseq rev-slog 1)) slogi) ; Все кроме гласной добавляем в новый слог
(setf current-slog (list char))) ; Начинаем новый слог
(t ; Во всех остальных случаях
; Просто отделяем слог
(push (reverse (subseq rev-slog 1)) slogi) ; Добавляем слог
(setf current-slog (list char)))))))))) ; Начинаем новый слог
(reverse (mapcar #'(lambda (slog) (coerce slog 'string)) (reverse slogi))))) ; Преобразуем списки символов в строки и переворачиваем обратно
;;; Функция для разбиения фразы на слова (замена split-sequence)
(defun razbit-frazu-na-slova (fraza)
(let ((words nil)
(start 0)
(end nil))
(loop
(setf end (position #\Space fraza :start start)) ; Ищем пробел начиная с позиции start
(if end ; Если пробел найден
(progn
(push (subseq fraza start end) words) ; Извлекаем слово и добавляем в список
(setf start (1+ end))) ; Передвигаем start на позицию после пробела
(progn ; Если пробел не найден (последнее слово)
(push (subseq fraza start) words) ; Добавляем последнее слово
(return (nreverse words))))))) ; Переворачиваем список и возвращаем
;;; Главная функция, которая принимает фразу и выводит ее по слогам
(defun obrabotat-frazu (fraza)
(if (null fraza)
(format t "Фраза пуста.")
(format t "~{~{~A~^-~}~%~}" ; Выводим все слоги в одной строке, разделенные дефисами
(mapcar #'(lambda (slovo) (razbit-na-slogi slovo)) ; Для каждого слова разбиваем на слоги
(razbit-frazu-na-slova fraza))))) ; Разбиваем фразу на слова
;;; Пример использования:
;; (obrabotat-frazu "война майка")
(terpri) ; Перевод строки в конце