mod: изменен алгоритм переноса в русских словах (императивнный на декларативный) с весами и приоритетами
This commit is contained in:
@@ -143,60 +143,67 @@ class Hyphenator:
|
||||
|
||||
# Поиск допустимой позиции для переноса около заданного индекса
|
||||
def find_hyphen_point_ru(word_segment: str, start_idx: int) -> int:
|
||||
vow_indices = [i for i, char_w in enumerate(word_segment) if self._is_vow(char_w)]
|
||||
# Если в слове нет гласных, то перенос невозможен
|
||||
if not vow_indices:
|
||||
word_len = len(word_segment)
|
||||
min_part = self.min_chars_per_part
|
||||
|
||||
# --- Вложенная функция для оценки качества точки переноса ---
|
||||
def get_split_score(i: int) -> int:
|
||||
"""
|
||||
Вычисляет "оценку" для точки переноса `i`. Чем выше оценка, тем качественнее перенос.
|
||||
-1 означает, что перенос в этой точке запрещен.
|
||||
"""
|
||||
# --- Сначала идут ЗАПРЕТЫ (жесткие "нельзя") ---
|
||||
# Если правило нарушено, сразу дисквалифицируем точку.
|
||||
if self._is_sign(word_segment[i]) or self._is_j_sound(word_segment[i]):
|
||||
return -1 # ЗАПРЕТ 1: Новая строка не может начинаться с Ь, Ъ или Й.
|
||||
if self._is_j_sound(word_segment[i - 1]) and self._is_vow(word_segment[i]):
|
||||
return -1 # ЗАПРЕТ 2: Нельзя отрывать Й от следующей за ней гласной.
|
||||
# --- Теперь идут РАЗРЕШЕНИЯ с разными приоритетами ---
|
||||
# РАЗРЕШЕНИЕ 1: Перенос между сдвоенными согласными.
|
||||
if self._is_cons(word_segment[i - 1]) and word_segment[i - 1] == word_segment[i]:
|
||||
return 10
|
||||
# РАЗРЕШЕНИЕ 2: Перенос после "слога" с Ь/Ъ, если дальше идет СОГЛАСНАЯ.
|
||||
# Пример: "строитель-ство", но НЕ "компь-ютер".
|
||||
# По-хорошему нужно проверять, что перед Ь/Ъ нет йотированной гласной
|
||||
if self._is_sign(word_segment[i - 1]) and self._is_cons(word_segment[i]):
|
||||
return 9
|
||||
# РАЗРЕШЕНИЕ 3: Перенос после "слога" если предыдущий Й (очень качественный перенос).
|
||||
if self._is_j_sound(word_segment[i - 1]):
|
||||
return 7
|
||||
# РАЗРЕШЕНИЕ 4: Перенос между тремя согласными (C-CС), чуть лучше, чем после гласной.
|
||||
if self._is_cons(word_segment[i]) and self._is_cons(word_segment[i-1]) and self._is_cons(word_segment[i+1]):
|
||||
return 6
|
||||
# # РАЗРЕШЕНИЕ 5 (?): Перенос между согласной и согласной (C-C).
|
||||
# if self._is_cons(word_segment[i - 1]) and self._is_cons(word_segment[i]):
|
||||
# return 5
|
||||
# РАЗРЕШЕНИЕ 6 (Основное правило): Перенос после гласной.
|
||||
if self._is_vow(word_segment[i - 1]):
|
||||
return 5
|
||||
|
||||
|
||||
# Если ни одно правило не подошло, точка не подходит для переноса.
|
||||
return 0
|
||||
|
||||
# 1. Собираем всех кандидатов и их оценки
|
||||
candidates = []
|
||||
possible_indices = range(min_part, word_len - min_part + 1)
|
||||
for i in possible_indices:
|
||||
score = get_split_score(i)
|
||||
if score > 0:
|
||||
# Добавляем только подходящих кандидатов
|
||||
distance_from_center = abs(i - start_idx)
|
||||
candidates.append({'score': score, 'distance': distance_from_center, 'index': i})
|
||||
|
||||
# 2. Если подходящих кандидатов нет, сдаемся
|
||||
if not candidates:
|
||||
return -1
|
||||
word_segment_len = len(word_segment)
|
||||
# Ищем ближайшую гласную до или после start_idx
|
||||
for i in vow_indices:
|
||||
if i >= start_idx - self.min_chars_per_part and i + self.min_chars_per_part < word_segment_len:
|
||||
# Проверяем, что после гласной есть минимум символов "хвоста"
|
||||
ind = i + 1
|
||||
# 1. Не отделяем "хвостов" с начала или конца (это некрасиво)
|
||||
if ind <= self.min_chars_per_part or ind >= word_segment_len - self.min_chars_per_part:
|
||||
continue
|
||||
# 2. Сдвигаем перенос за мягкий/твердый знак, если он сразу за согласной (ГОСТ 7.62-2008)
|
||||
if self._is_sign(word_segment[ind]):
|
||||
# 2.1 Текущая буква мягкий/твердый знак. Ставим перенос за ней (индекс ind+1).
|
||||
return ind + 1
|
||||
if (self._is_cons(word_segment[ind]) and
|
||||
i+1 < word_segment_len and self._is_sign(word_segment[ind+1])):
|
||||
# 2.2 Текущая буква согласная, а следующая мягкий/твердый знак. Ставим перенос за ней
|
||||
return ind+2
|
||||
# 3. Проверка на `Й` (полугласная). Не бывает слов, когда сразу не ней идет гласная,
|
||||
# или перед ней идет согласная. Сдвигает перенос за полугласную букву если она идет после
|
||||
# гласной.
|
||||
if self._is_j_sound(word_segment[ind+1]):
|
||||
# 3.1 Текущая буква `й`. Ставим за ней перенос (индекс ind+1).
|
||||
return ind+1
|
||||
if (self._is_vow(word_segment[ind]) and
|
||||
i+1 < word_segment_len and self._is_j_sound(word_segment[ind+1])):
|
||||
# 3.2 Текущая буква гласная, а следующая `й`. Ставим перенос за `й` (индекс ind+2).
|
||||
# Ставим перенос за `й` (индекс ind+2).
|
||||
return ind+2
|
||||
# 4. Проверка на сдвоенная-согласная (C-C).
|
||||
if (self._is_cons(word_segment[ind]) and
|
||||
i+1 < word_segment_len and word_segment[ind] == word_segment[ind+1]):
|
||||
print("сдвоенная согласная")
|
||||
# 4.1 Текущая буква согласная и следующая така же (сдвоенная согласная). Ставим перенос
|
||||
# за ней (индекс ind+1).
|
||||
return ind + 1
|
||||
if (self._is_cons(word_segment[ind]) and
|
||||
i+1 < word_segment_len and self._is_cons(word_segment[ind+1])):
|
||||
# 4.2 НЕ ОБЯЗАТЕЛЬНОЕ ПРАВИЛО: Текущая буква согласная, а следующая тоже согласная.
|
||||
# Ставим перенос за ней (индекс ind+1).
|
||||
return ind+1
|
||||
# 5. Проверка на гласная-гласная (V-V).
|
||||
if (self._is_vow(word_segment[ind]) and
|
||||
i+1 < word_segment_len and self._is_vow(word_segment[ind+1])):
|
||||
# 5.1 Текущая буква гласная, а следующая гласная. Перенос не делаем. Возможно,
|
||||
# надо дальше искать до ближайшей согласной, но это усложнит алгоритм.
|
||||
continue
|
||||
# 6. TODO (опционально): Проверка на суффикс и приставку (не разбивать). Нужен словарь.
|
||||
# 7. TODO (опционально): Проверка на короткий корень (не разбивать). Нужен очень большой словарь.
|
||||
return ind
|
||||
return -1 # Не нашли подходящую позицию
|
||||
|
||||
# 3. Сортируем кандидатов: сначала по убыванию ОЦЕНКИ, потом по возрастанию УДАЛЕННОСТИ от центра.
|
||||
# Это гарантирует, что перенос "н-н" (score=10) будет выбран раньше, чем "е-н" (score=5),
|
||||
# даже если "е-н" чуть ближе к центру.
|
||||
best_candidate = sorted(candidates, key=lambda c: (-c['score'], c['distance']))[0]
|
||||
|
||||
return best_candidate['index'] # Не нашли подходящую позицию
|
||||
|
||||
# Рекурсивное деление слова
|
||||
def split_word_ru(word_to_split: str) -> str:
|
||||
|
Reference in New Issue
Block a user