Алгоритм получился такой (привожу практически тот самый Python-код, который я написал карандашом тогда на занятии).
slog_start = 0
i = 0
while i < len(word):
if word[i] in vowels_set:
vowel_pos = i
i += 1
while i < len(word):
if word[i] in vowels_set:
if i - vowel_pos == 1:
hyphens.append(i)
elif i - vowel_pos == 2:
hyphens.append(i - 1)
else:
hyphens.append(vowel_pos + 2)
На этом месте меня осенило: достаточно просто знать расстояние между соседними гласными буквами (пусть они будут в позициях a и b) — если оно равно 1, тогда вставляем перенос в позиции b, если равно 2, то в позиции b − 1, иначе [т.е. когда расстояние больше 2] в позиции a + 2.
Получается такой Python-код:
word = input()
vowels_set = set('аеёиоуыэюяАЕЁИОУЫЭЮЯ')
vowels = []
for i in range(len(word)):
if word[i] in vowels_set:
vowels.append(i)
import collections
hyphens = collections.deque()
for i in range(1, len(vowels)):
a, b = vowels[i-1], vowels[i]
if b - a == 1:
hyphens.append(b)
elif b - a == 2:
hyphens.append(b - 1)
else:
hyphens.append(a + 2)
for i in range(len(word)):
if len(hyphens) and hyphens[0] == i:
print('-', end = '')
hyphens.popleft()
print(word[i], end = '')
word = input()
vowels_set = set('аеёиоуыэюяАЕЁИОУЫЭЮЯ')
prev_vowel = len(word)
for i in range(len(word)):
if word[i] in vowels_set:
prev_vowel = i
break
import collections
hyphens = collections.deque()
for i in range(prev_vowel + 1, len(word)):
if word[i] in vowels_set:
a, b = prev_vowel, i
if b - a == 1:
hyphens.append(b)
elif b - a == 2:
hyphens.append(b - 1)
else:
hyphens.append(a + 2)
prev_vowel = i
for i in range(len(word)):
if len(hyphens) and hyphens[0] == i:
print('-', end = '')
hyphens.popleft()
print(word[i], end = '')
Осталось только добавить поддержку букв «й», «ь» и «ъ».
Для этого нужно слегка модифицировать цепочку условий начинающуюся с
if b - a == 1:
:for i ...:
...
if b - a == 1:
hyphens.append(b)
else:
for j in reversed(range(a + 1, b)):
if word[j] in specials_set: # specials_set = set('йьъЙЬЪ')
hyphens.append(j + 1)
break
else:
if b - a == 2:
hyphens.append(b - 1)
else:
hyphens.append(a + 2)
word = input()
vowels_set = set('аеёиоуыэюяАЕЁИОУЫЭЮЯ')
specials_set = set('йьъЙЬЪ')
prev_vowel = len(word)
for i in range(len(word)):
if word[i] in vowels_set:
prev_vowel = i
break
pos = 0
for i in range(prev_vowel + 1, len(word)):
if word[i] in vowels_set:
a, b = prev_vowel, i
if b - a == 1:
print(word[pos:b], end = '-')
pos = b
else:
for j in reversed(range(a + 1, b)):
if word[j] in specials_set:
print(word[pos:j + 1], end = '-')
pos = j + 1
break
else:
if b - a == 2:
print(word[pos:b - 1], end = '-')
pos = b - 1
else:
print(word[pos:a + 2], end = '-')
pos = a + 2
prev_vowel = i
print(word[pos:])
В заключение отвечу на возможный вопрос «и к чему всё это?», ведь есть же алгоритм П. Христова в модификации Дымченко и Варсанофьева, который, к тому же, применяется не только для русского, но и для английского языка. Ну, во-первых, по факту для английского он не подходит из-за особенностей этого языка. Во-вторых, некоторые правила в нём довольно сомнительны, например правило "гсс-ссг" приводит к неверной разбивке слова "отстранять". Ну и в-третьих, предложенный мной алгоритм существенно быстрее.
P. S. Кстати, буду признателен если кто даст ссылочку на оригинальный алгоритм П. Христова, т.к. интересно какие именно модификации сделали Дымченко и Варсанофьев.
P. P. S. Поиском по списку всех русских слов обнаружилось не очень большое количество слов с 5-ю подряд идущими согласными буквами {например: агентство, ангстрем, бодрствование, интеллигентство}. В таких случаях (т.е. когда расстояние между соседними гласными буквами равно 6) следует вставлять перенос в позиции a + 3 или [что то же самое] b − 3.
Также можно объединить случаи, когда расстояние между гласными равно 1 или 2: в обоих этих случаях перенос вставляется в позиции a + 1.
vowels_set = set('аеёиоуыэюяАЕЁИОУЫЭЮЯ')
specials_set = set('йьъЙЬЪ')
word = input()
prev_vowel = len(word)
for i in range(len(word)):
if word[i] in vowels_set:
prev_vowel = i
break
pos = 0
for i in range(prev_vowel + 1, len(word)):
if word[i] in vowels_set:
a, b = prev_vowel, i
for j in reversed(range(a + 1, b)):
if word[j] in specials_set:
npos = j + 1
break
else:
if b - a <= 2:
npos = a + 1
elif b - a >= 6:
npos = b - 3
else:
npos = a + 2
print(word[pos:npos], end = '-')
pos = npos
prev_vowel = i
print(word[pos:])
Комментарии (27)
Paulus
31.05.2023 21:49В прошлом веке, когда python ещё не было, что что ТеХ, что Word обходились мягкими переносами. Проще некуда и все языки поддерживались ;)
Wesha
31.05.2023 21:49+4Если я что-то в этой жизни усвоил, так это то, что "у любой задачи есть простое, элегантное, неправильное решение" (c)
Примеры, на которых Ваш код работает неверно: агентство, контрстрахование
alextretyak Автор
31.05.2023 21:49-2Почему неверно?
У меня разделяет так: а-гент-ство, контр-стра-хо-ва-ни-е.
Вы точно использовали последнюю версию кода (которая в самом конце статьи под P. P. S. внутри спойлера)?Wesha
31.05.2023 21:49Хм, Вы бы написали, которая из них "последняя". Я взял ту, которая перед обработкой мягкого знака (и убедился, что в словах этого знака нет).
osmanpasha
31.05.2023 21:49И есть ещё слова, где небольшое число согласных между гласными (1), но граница слогов проходит по границе частей слова, а не после гласной: под-одеяльник, пред-усилитель
vesper-bot
31.05.2023 21:49+1Опа, а это для меня новость. Откуда инфа?
osmanpasha
31.05.2023 21:49+1Ох, пошел гуглить и, теперь кажется, что я не прав. Разделение этих слов я проверил по викисловарю: пододеяльник, предусилитель, но теперь меня терзают сомнения, а разделение ли на слоги там указано, или разделение для переноса слова. Т.к. это иногда разные вещи, и вообще разделение на слоги - сложная и дискуссионная тема, у которой есть несколько школ. По крайней мере, разделение на слоги по правилам Московской фонологической школы будет "нормальное": по-додеяльник.
Процитирую http://new.gramota.ru/spravka/buro/search-answer?s=249871
В первую очередь отметим, что проблема слогораздела является одной из наиболее сложных в современной лингвистике и до конца не решенной. Это связано с отсутствием единого понимания сущности слога. Невозможность зафиксировать признаки слога как единого целого, фонетическая невыраженность границы между слогами приводит некоторых лингвистов к мысли, что слогораздела в русском языке вообще не существует. Ряд исследователей и сам слог называют «фикцией».
Тем не менее наиболее распространены в современной русистике две теории слога. Они связаны с именами двух выдающихся советских языковедов – Р. И. Аванесова (Московская фонологическая школа) и Л. В. Щербы (Ленинградская фонологическая школа). Правила деления на слоги в этих двух теориях несколько различаются (так, слово кошка следует делить на слоги согласно теории Аванесова так: ко-шка, согласно теории Щербы так: кош-ка). Поэтому в разных учебниках правила слогоделения могут быть сформулированы по-разному, в зависимости от того, позицию какой фонологической школы разделяет автор учебника.
А вот правила переноса более строго кодифицированы, и в текущей редакции предпочтительно "под-одеяльник", но допуситмо и "по-додеяльник". Ранее правильным был только первый вариант.
vesper-bot
31.05.2023 21:49По правилам русского языка, запомненным мной во втором классе, точка разделения слогов находится перед слиянием (согласная+гласная) следующего слога, или между двумя гласными, если слияния в следующем слоге нет. Всё.
Hardcoin
31.05.2023 21:49Во втором классе не проходят слово "агентство". Оно по этому правилу поделится неверно, но для второклассника не страшно.
vesper-bot
31.05.2023 21:49Кстати, "агентство" делится на слоги как раз с длинным хвостом из согласных, "а-гентст-во". Мы с учителем заспорили по поводу слова "уползла", у которого тоже длинный (для тогдашних примеров, две против максимум одной) хвост во втором слоге, и я спор успешно выиграл, она делила как "у-пол-зла".
Wesha
31.05.2023 21:49Во втором классе не проходят слово "агентство".
Это зависит от того, где мама работает!
alextretyak Автор
31.05.2023 21:49точка разделения слогов находится перед слиянием (согласная+гласная) следующего слога
Тогда получится отст-ра-нять.
И такое правило не учитывает присутствие в слове твердого или мягкого знаков, а также буквы «й» (подъезд, бульон, майор).vesper-bot
31.05.2023 21:49+1Ну, твердый и мягкий знаки не образуют слияний (и строго говоря, не помню, чтобы их отмечали как согласные). То есть, в этом случае разделение на слоги будет перед гласной. Согласен, формулировку надо подправить, не "между двумя гласными", а "перед гласной". Про Й плюс гласную посложнее, но сколько помню, правильно как раз будет ма-йор, а не май-ор.
Апд: про Й был неправ, а вот про всё остальное, похоже, сами правила сменились, причем кардинально, но если честно, что в старой, что в новой системе правил даже на втором уровне есть вариативность, что мягко скажем нелогично для процесса, который априори должен быть детерминирован.
alextretyak Автор
31.05.2023 21:49То есть, в этом случае разделение на слоги будет перед гласной.
Перед гласной, говорите? А как же: дяденька, большой, скользкий?
правильно как раз будет ма-йор, а не май-ор.
А здесь утверждается наоборот.
vesper-bot
31.05.2023 21:49А как же: дяденька, большой, скользкий?
Слияние есть - фиг с мягким знаком, по нему и делим. Вот если нет, там грабли. Но в соседней ветке напомнили контрпример (кстати), по которому приставки запихиваются в один слог (пред-усилитель), и я склонен с этим согласиться, слова с приставками следует рассматривать так, как если бы граница слога уже стояла по разделу между приставкой и корнем (или приставками, в словах с несколькими приставками). То есть тот же "подъезд" прекрасно делится по приставке, "подъ-езд", и заморочек с обходом твердого знака не требуется. Вопрос тогда в слове "контракт", его как делить, "контр-акт" или "конт-ракт", или вообще "кон-тракт"?
Wesha
31.05.2023 21:49дяденька, большой, скользкий?
...одна штука!
(Простите,
погорячилсяне удержался.)
alcanoid
31.05.2023 21:49+1Это правила переноса, и они не полностью совпадают с разбиением по слогам.
osmanpasha
31.05.2023 21:49+1По ссылке, кстати, правила переноса слов, а не слогоделения. Судя по всему, именно слогоделение пользователям языка не так важно, интересно только с академической точки зрения и до сих пор является дискуссионной темой.
apachik
печально, что не выложили тесты (а есть ли они вообще?)
без них "итоговый код" не выглядит "полным"
Terimoun
Полностью согласен, итоговый код не помешал бы