Архив метки: алгоритм

Генерация множественных числительных в согласовании с существительным на python

Суть задачи: написать функцию, на входе у которой число и существительное, на выходе сгенерированное числительное и существительное в правильном склонении.
Такая задача очень часто возникает например в финансовой сфере, так как на документах принято писать сумму помимо цифрой — ещё и прописью.

Алгоритм склонения множественно числа очень прост. Всего существует только три варианта для любого числа. Возьмём слово доллар, тогда
Вариант 1: именительный падеж, например один доллар, тридцать один доллар, много-много-один доллар. т.е. когда число оканчивается на 1
Вариант 2: Родительный падеж, например два доллара, тридцать три доллара, много-много-четыре доллара. т.е. когда число оканчивается на 2, 3 и 4
Вариант 3: Множественная форма, Родительный падеж. например пять долларово, много-много-восемь долларов. т.е. когда число оканчивается на 5, 6, 7, 8, 9, 0
Случай с нулём лучше рассмотреть отдельно в силу различных требований от того что вы действительно хотите получить.

Внимание! Для чисел оканчивающихся на 11, 12, 13, 14 есть особенность. Поэтому
основной алгоритм выбора формы существительного после числительного будет таким:
Шаг 1: Если число оканчивается на 1, но не оканчивается на 11, то вариант 1 (Именительный падеж)
Шаг 2: Если число оканчивается на 2, 3, 4, и не оканчивается на 12, 13, 14, то вариант 2 (Родительный падеж)
Шаг 3: Всё остальное — вариант 3 (Множественный родительный падеж)

Теперь вернемся к нашей изначальной задаче, ведь на входе у нас есть только одно существительное. Значит надо уметь его поставить в одну из этих форм. Воспользуемся библиотекой на python — pymorphy2 :

import pymorphy2

morph = pymorphy2.MorphAnalyzer()
word = morph.parse('доллар')[0]
v1, v2, v3 = word.inflect({'sing', 'nomn'}), word.inflect({'gent'}), word.inflect({'plur', 'gent'})

print(v1.word, v2.word, v3.word)  # доллар доллара долларов

Если кто знаком с библиотекой pymorphy2, то возможно знают, что можно напрямую воспользоваться методом make_agree_with_number . Это действительно так, и не надо воротить лишний код, но есть одно Но — библиотека не умеет генерировать сами числительные, а лишь согласовывать, ставя в нужную форму
существительное. Чтобы именно генерировать — воспользуемся библиотекой, найденной мной на github — https://github.com/seriyps/ru_number_to_text/blob/master/num2t4ru/__init__.py (ru_number_to_text-master). Отмечу, что библиотека делает даже чуть больше чем генерация целых числительных, также возможна генерация дробных числительных. И так, создадим финальную функцию, с импортом указанной библиотеки

from num2t4ru import num2text
import pymorphy2

def get_number_and_noun(numeral, noun):
    morph = pymorphy2.MorphAnalyzer()
    word = morph.parse(noun)[0]
    v1, v2, v3 = word.inflect({'sing', 'nomn'}), word.inflect({'gent'}), word.inflect({'plur', 'gent'})
    return num2text(num=numeral, main_units=((v1.word, v2.word, v3.word), 'm'))

result = get_number_and_noun(123452, 'доллар')  
print(result)  # сто двадцать три тысячи четыреста пятьдесят два доллара

В качестве практического применения сгенерированных числительных — представьте что вам надо создать много документов word, в каждый из которых требуется подставить полученные числа. Причем сгенерированные числа с согласуемым существительным можно в дальнейшем просклонять с помощью pymorphy2. (в текущем примере числительные в именительном падеже)