Сообщество Engee

Автограмма

Автор
avatar-alexevsalexevs
Notebook

Проверка автограмм на языке Julia

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

Введение

Что такое автограмма?

Автограмма — это особый тип автологического (автоописательного) предложения, которое описывает собственную структуру. А именно, в таком предложении указывается, сколько раз в нём встречается каждая буква алфавита, знаки препинания и другие символы. Автограмма корректна, если счётчик каждого упомянутого элемента совпадает с его реальным количеством в этом же предложении.

Например, автограмма может говорить: "В этом предложении шесть букв 'е'", и если в нём действительно содержится шесть букв 'е', то это часть корректного описания.

Цель алгоритма

Цель реализации — проверить, корректно ли автограмма описывает саму себя, то есть:

  1. Для каждой упомянутой буквы или символа: указанное количество совпадает с реальным.
  2. Все упомянутые в предложении символы действительно учтены.
  3. Учитываются ли знаки препинания, если это требуется.

Такой алгоритм может использоваться в играх, лингвистических задачах, генерации текста и даже как интересный способ саморефлексии в искусственном интеллекте.

Основная часть

Подключение необходимых пакетов

In [ ]:
# Установка необходимого пакета DataStructures, если он не установлен
import Pkg; Pkg.add("DataStructures")

# Использование пакета DataStructures для удобного подсчёта частоты символов
using DataStructures
   Resolving package versions...
   Installed FiniteDiff ─ v2.27.0
    Updating `~/.project/Project.toml`
 [864edb3b] + DataStructures v0.18.22
  No Changes to `~/.project/Manifest.toml`
WARNING: using DataStructures.reset! in module Main conflicts with an existing identifier.

Определение словаря чисел в текстовом виде

In [ ]:
# Словарь с текстовым представлением чисел от одного до девяносто
const textnumbers = Dict(
    "single" => 1, "one" => 1, "two" => 2, "three" => 3, "four" => 4,
    "five" => 5, "six" => 6, "seven" => 7, "eight" => 8, "nine" => 9,
    "ten" => 10, "eleven" => 11, "twelve" => 12, "thirteen" => 13,
    "fourteen" => 14, "fifteen" => 15, "sixteen" => 16,
    "seventeen" => 17, "eighteen" => 18, "nineteen" => 19,
    "twenty" => 20, "thirty" => 30, "forty" => 40, "fifty" => 50,
    "sixty" => 60, "seventy" => 70, "eighty" => 80, "ninety" => 90
)
Out[0]:
Dict{String, Int64} with 28 entries:
  "fourteen" => 14
  "seventy"  => 70
  "twelve"   => 12
  "eight"    => 8
  "twenty"   => 20
  "one"      => 1
  "sixteen"  => 16
  "eighteen" => 18
  "seven"    => 7
  "thirty"   => 30
  "six"      => 6
  "five"     => 5
  "forty"    => 40
  "ten"      => 10
  "sixty"    => 60
  "nineteen" => 19
  "fifty"    => 50
  "thirteen" => 13
  "ninety"   => 90
  "three"    => 3
  "single"   => 1
  "fifteen"  => 15
  "eleven"   => 11
  "two"      => 2
  "four"     => 4
  ⋮          => ⋮

Этот словарь связывает строковые представления чисел (пример: "five", "twenty-five") с их целочисленными значениями. Используется для преобразования чисел, описанных словами, в конкретные числовые значения.

Функция преобразования текста в число

In [ ]:
"""
    phrasetointeger(txt)

Преобразование текстовых чисел в числовое значение.
Пример: "twenty five" => 25
"""
function phrasetointeger(txt)
    words = split(txt, r"\W+")      # Разбивка строки на отдельные слова, исключая знаки препинания
    n = 0                           # Накопитель числа
    for w in words                  # Проход по всем словам
        n += get(textnumbers, w, 0) # Если слово есть в словаре, добавляем его значение
        w == "hundred" && (n *= 100) # Если слово сотня — умножаем на 100
    end
    return n                        # Возвращаем общее числовое значение
end
Out[0]:
phrasetointeger

Эта функция принимает строку текста, вычленяет из неё числовые слова и составляет из них целое число от 1 до 999. Важно, что функция поддерживает составные числа, если они написаны через пробел (например, "eleven" или "twenty five").

Основная функция проверки автограммы

In [ ]:
"""
    isautogram(txt, countpunctuation; verbose = true)

Функция проверяет, является ли строка txt корректной автограммой.
Аргументы:
- txt: текст, который нужно проверить
- countpunctuation: считать ли знаки препинания (true/false)
- verbose: выводить ли сообщения об ошибках
"""
function isautogram(txt, countpunctuation; verbose = true)
    # Приводим весь текст к строчным буквам
    s = lowercase(txt)
    
    # Считаем количество вхождений каждого символа
    charcounts = counter(s)

    # Словарь символов, о которых нужно проверить упоминание
    stillneedmention = Dict(
        p[1] => isletter(p[1]) || p[1] != ' ' && countpunctuation ? p[2] : 0
        for p in charcounts
    )

    # Небольшое форматирование строки перед разбором
    s = " " * replace(s, r"^\.(?:employs|composed|contains)" => "")

    # Разбиваем строку по запятым и двоеточиям — на токены, описывающие символы
    for mention in split(s, r"\s*,|:\s*")
        mention = replace(mention, r" and$" => "")  # Удаляем слово "and" в конце строки
        spos = findlast(isspace, mention)           # Находим последний пробел — далее будет слово символа

        if spos === nothing continue end            # Если не нашли — пропускаем

        # Извлекаем числовую часть текста (до последнего пробела)
        numfromtext = phrasetointeger(mention[begin:spos-1])
        numfromtext == 0 && continue  # Если число 0 — продолжаем

        # Извлекаем часть строки, обозначающую символ
        c = mention[begin+spos:end]

        if c == "letters"
            # Проверка общего количества букв
            if numfromtext != count(isletter, txt)
                verbose && println("The total letter count (should be $(count(isletter, txt))) is incorrect.")
                return false
            end
            continue
        end

        # Определяем символ по описанию
        ch = contains(c, "comma") ? ',' : 
             contains(c, "apostrophe") ? '\'' : 
             contains(c, "hyphen") ? '-' : Char(c[1])

        # Проверка количества упомянутого символа
        if charcounts[ch] == numfromtext
            stillneedmention[ch] = 0  # Помечаем как проверенный
        else
            verbose && println("The count of $ch in the phrase is incorrect.")
            return false
        end
    end

    # Проверяем, не осталось ли символов, упоминание которых забыто
    for p in stillneedmention
        if p[2] > 0
            verbose && println("The letter and count $p was not mentioned in the counts in the phrase.")
            return false
        end
    end

    return true  # Если всё проверено и совпадает — автограмма корректна
end
Out[0]:
isautogram

Эта основная функция работает по следующим этапам:

  1. Нормализует текст (все буквы строчные).
  2. Считает частоту всех символов.
  3. Обходит все упоминания символов в тексте и проверяет числовые значения.
  4. Проверяет, все ли реально встречающиеся символы учтены в тексте.
  5. Возвращает true, если всё корректно, иначе — false.

Если verbose = true, выводятся поясняющие сообщения об ошибках.

Тестирование автограмм

In [ ]:
# Набор тестовых строк — автограмм и не автограмм
for (i, t) in enumerate([
    ("This sentence employs two a's, two c's, two d's, twenty-eight e's, five f's, three g's, eight h's, eleven i's, three l's, two m's, thirteen n's, nine o's, two p's, five r's, twenty-five s's, twenty-three t's, six v's, ten w's, two x's, five y's, and one z.", false),
    ("This sentence employs two a's, two c's, two d's, twenty eight e's, five f's, three g's, eight h's, eleven i's, three l's, two m's, thirteen n's, nine o's, two p's, five r's, twenty five s's, twenty three t's, six v's, ten w's, two x's, five y's, and one z.", false),
    ("This pangram contains four as, one b, two cs, one d, thirty es, six fs, five gs, seven hs, eleven is, one j, one k, two ls, two ms, eighteen ns, fifteen os, two ps, one q, five rs, twenty-seven ss, eighteen ts, two us, seven vs, eight ws, two xs, three ys, & one z.", false),
    ("This sentence contains one hundred and ninety-seven letters: four a's, one b, three c's, five d's, thirty-four e's, seven f's, one g, six h's, twelve i's, three l's, twenty-six n's, ten o's, ten r's, twenty-nine s's, nineteen t's, six u's, seven v's, four w's, four x's, five y's, and one z.", false),
    ("Thirteen e's, five f's, two g's, five h's, eight i's, two l's, three n's, six o's, six r's, twenty s's, twelve t's, three u's, four v's, six w's, four x's, two y's.", false),
    ("Fifteen e's, seven f's, four g's, six h's, eight i's, four n's, five o's, six r's, eighteen s's, eight t's, four u's, three v's, two w's, three x's.", true),
    ("Sixteen e's, five f's, three g's, six h's, nine i's, five n's, four o's, six r's, eighteen s's, eight t's, three u's, three v's, two w's, four z's.", false),
   ])
    
    # Проверяем каждую строку
    println("Test phrase $i is", isautogram(t[1], t[2]) ? " " : " not ", "a valid autogram.\n")
end
Test phrase 1 is a valid autogram.

Test phrase 2 is a valid autogram.

Test phrase 3 is a valid autogram.

Test phrase 4 is a valid autogram.

Test phrase 5 is a valid autogram.

The letter and count '\'' => 14 was not mentioned in the counts in the phrase.
Test phrase 6 is not a valid autogram.

The count of z in the phrase is incorrect.
Test phrase 7 is not a valid autogram.

Здесь осуществляется проверка семи примеров текстов, некоторые из которых являются автограммами, а некоторые — нет. Параметр t[2] указывает, нужно ли учитывать знаки препинания при проверке.

Заключение

Мы рассмотрели реализацию алгоритма проверки корректности автограмм на языке программирования Julia. Мы использовали внешний пакет DataStructures для подсчёта частоты символов, реализовали функции:

  • Преобразования словесных чисел в числовые.
  • Проверки корректности описания каждого символа.
  • Проверки полноты упоминания всех символов, присутствующих в предложении.

Этот алгоритм может применяться в задачах обработки естественного языка, автоматической проверке логической корректности текстов, а также может служить примером интересных трюков в области программирования и лингвистики.

Программа прошла тестирование на нескольких примерах, включая как корректные, так и некорректные автограммы, что подтверждает её работоспособность.