Программирование на языке пролог для искусственного интеллекта 2а: компьютерные стихи


Лебедь на небе летит,
Ветерок в лесах шумит.
Ранняя звезда блистает,
А кораблик уплывает.
Грозы в облаке гремят,
В небе лебеди летят.
Воздух ласково ласкает,
Берег вдалеке сияет.


Небо радостно горит,
Лес прекрасный шелестит.
Пчёлка по небу кружится,
Мишка по тропинке мчится.
Теремок трубой дымит,
Чуткая сова сидит.
А лесной ручей струится,
Жгучье солнышко садится.


Дева милая вздыхает,
Высоко луна летает.
Щёки на лице горят,
Очи жемчугом блестят.
Теремки стоят пустые,
Сохнут капли голубые.
Да в груди любовь поёт,
Голос бархатный влечёт.

1. Причины опубликования статьи
В интернете программ написания стихов не мало, но с открытым кодом и написанных на Прологе я не встречал. Почему и пожелал выложить здесь данную статью, надеясь и сам что-то узнать от других. Кроме того, программа запускается в приложении работающем на Андроиде (см. статью о приложении Prolog Classic [1]), что для многих может быть удобно.

2. Алгоритм программы
Способы алгоритмизации сочинения компьютером стихов бывают разные:

  1. Иметь заранее готовые рифмованные строчки (и даже целые строфы [2]), среди которых, при сочинении стиха, компьютер выбирает нужные ему, в зависимости, например, от пожеланий автора [3] (такой способ большого разнообразия не даёт, по причине жёстко сформированных неизменных строк);
  2. Построение порядка слов (при построении строки) с предсказанием каждого слова за счёт известных предыдущих слов, (используется с нейронными сетями), а затем попытка рифмовки слов [4] (при таком подходе, те строки, словосочетания, которые чаще встречались при обучении нейросети будут в первую очередь рассматриваться, что, опять же, большого разнообразия не даёт).

Я пошёл иным путём.
Мой алгоритм такой: компьютер, при помощи набора “возможных словосочетаний” для прилагательных, глаголов, наречий и дополнений (из файла составленного мной и по желанию может быть дополнен пользователем), составляет случайные строки с существительными-объектами из списка подходящих под определённую тему, а затем пробует их рифмовать; в случае неудачи, составляет другие строки и пробует по-новой.
Файл “возможных словосочетаний” - это набор предикатов, где имеются связи:

  1. Существительного с прилагательными. Например:
    сущ_прилаг( заря, [ алый, румяный, утренний ] ).;
  2. Существительного с глаголами. Например:
    сущ_глагол( ткачиха, [ глядеть, сидеть, плакать ] ).;
  3. Существительного и глагола с наречиями. Например:
    сущ_гл_нареч( сова, парить, [ высоко, прекрасно, вдали, низенько, низко ] ).;
  4. Существительного и глагола с дополнениями. Например:
    сущ_гл_доп( звезда, светиться, [ прп( на, небо ), прп( в, небо ) ], [] ).;
    сущ_гл_доп( дева, напевать, [], [ винп( песня ) ] )..

Такие связи нужны для того, чтобы небыло бредовых выражений, типа: жаренная заря; ткачиха кипит; сова парит холодно; звезда светится на ветке и т.п.
В этом словаре записаны много литературных эпитетов (прилагательные и наречия) [5, Эпитет, Стр.55]. Это нужно для того, чтобы стихотворения звучали поэтично, а не как обыденная речь. Однако они не используются при каждом возможном слове, такой стих может вызвать отвращение (это субъективное моё мнение).
Для заполнения такого файла я использовал написанную мной программу на прологе, которая переводила предложения (например: буйный ветер по морю мчится) в предикаты с сохранением в файл, исключая повторения. Также я использовал написанные мной программы, на прологе, в остальных файлах, для простановки ударений и других атрибутов (правильно было проставлено программами - больше 50%), после чего поправлял неправильные места вручную.
Для работы, программа пользуется не только файлом “возможных словосочетаний”, но и файлами-словарями: существительных, глаголов, прилагательных, наречий.
Эти файлы представляют из себя:

  1. Файл существительных - это набор предикатов, где первый аргумент предиката - “основа” существительного (не путать с основой в грамматике русского языка), за которым следует номер ударного гласного, а затем список “окончаний” для каждого падежа (не путать с окончаниями в грамматике русского языка); после род; затем тоже самое для множественного числа. Например (в виде прологовского терма):
    сущ( неб, 1, [ о, а, у, о, ом, е ], срр, небес, 3, [ а, сущ( небес, 2 ), ам, а, ами, ах ] ).;
  2. Файл глаголов - это набор предикатов, где первый аргумент - глагол в инфинитиве, затем глагол в настоящем времени, затем его ударение, после тоже самое для множественного числа. Глаголы здесь записаны в основном непереходные, но есть и такие, которые могут быть как непереходными, так и переходными (но нет глаголов, которые бывают только переходными - это упрощает работу, учитывая слабые технические характеристики сегодняшних гаджетов) . Например (в виде прологовского терма):
    глагол_наст( жить, живёт, 2, живут, 2).;
  3. Файл прилагательных - это набор предикатов, где первый аргумент предиката - само прилагательное, а второй - его ударение. Например (в виде прологовского терма):
    прилаг( досадливый, 2 ).;
  4. Файл наречий. Наречия, у меня, составляются из прилагательных и существительных, но есть и исключения, которые и вписаны в данном файле. Файл представляет из себя набор предикатов, где первый аргумент предиката - само наречие, а второй - его ударение. Например (в виде прологовского терма):
    нареч( вдаль, 1 )..

Итак, для сочинения стихов я использовал размер и рифмовку как во многих сказках Пушкина (сказка о царе Салтане; сказка о золотом петушке; Сказка о мёртвой царевне и о семи богатырях и др.):

  1. размер - 4-ёх стопный хорей: полный (8 гласных) и неполный (7 гласных);
  2. каждые две идущие друг за другом строки рифмуются между собой: 1 со 2, 3 с 4 и т.д.;
  3. рифмующиеся строки имеют одинаковое кол-во слогов (то-есть гласных);
  4. в рифмовке используется не жёсткая, но и не сильно слабая рифма:
    a) ударные гласные в конце строк должны быть либо одинаковыми, либо парными (например: а-я или о-ё);
    b) окончания после ударных гласных должны быть либо равны, либо буквы между друг другом должны быть парными. Й - пропускается, но не в конце.

Важная вещь - рифмующиеся слова (в конце строк) не должны быть одинаковыми (могут быть одинаковыми только, если они не одной и той же части речи, кроме существительного с дополнением). Это поэтическая безграмотность - не соблюдать этого правила.

Стихи, которые сочиняет компьютер, представляют из себя “пейзажную” лирику (то-есть изображение увиденного или воображаемого в природе), местами с восхищением ею. Например:
Грозовое небо блещет,
Ветренное море плещет.
Пухом облако летит,
Судно весело бежит.

Описательный способ, которым компьютер сочиняет стихи, подходит для описания не только природы, но и романтической картины и даже чувств. Например:
Слёзы пламенные льются,
Щёки красные смеются.
Сердце пламенем горит,
И любовь ручьём бежит.

Каждыми 2-мя строками, стих описывает, то происходящее вверху, то происходящее внизу, или наоборот. Например:
Море синее шумит,
Небо алое горит.
или
Звёзды в синем небе блещут,
Волны в синем море хлещут.

Начинаться стих может 2 способами:

  1. с фона (если про море: море, небо; если про лес: лес, небо и т.п.);
  2. с каких-нибудь предметов (например: лебедь, корабль), но с обязательным использованием дополнения. От такой подробности в начале, не режет ухо однообразием стиха. Например:
    *****
    (неправильно)
    Очи чистые слезят,
    Губы вишнями блестят.
    Слёзы пламенные льются,
    Щёки красные смеются.
    *****
    (правильно)
    В лунном свете слёзы льются,
    В чистой деве чувства вьются.
    Губы бледные дрожат.
    Очи чёрные блестят.
    Слёзы пламенные льются,
    Щёки красные смеются.

3. Логика работы ИИ

  1. Прежде чем сочинить стихотворение, нужно выбрать тему, по которой программа создаст стих, а именно, задать 3 списка определяющие определённую тему. Первые 2 списка - это предметы, которые мы видим вверху, и те, что видим внизу. Третий список - это предметы, которые могут использоваться в качестве дополнения, чтобы не выходить, за рамки заданной темы. Например:
    L1 = [ солнце, солнышко, облако, птица, аист, воздух, сова, совёнок, пчела, пчёлка, ветер ].
    L2 = [ терем, теремок, лист, мишка, трава, травка, заяц, зайчик, ёж, ёжик, лис, ручей, бабочка ].
    L3 = [ небо, солнце, солнышко, облако, птица, аист, воздух, сова, совёнок, пчела, пчёлка, ветер, рассвет, вышина, лес, терем, теремок, лист, мишка, трава, травка, заяц, зайчик, ёж, ёжик, лис, ручей, бабочка, нога, земля, ветка, дерево, улыбка, тропинка, дорожка, изба, дуб, ёлка, оглядка, место, спина, берлога, листва, норка, живот, тепло, пенёк, друг, гора, речка ].
    Также, задаются 2 существительных - предметы фона, для создания первых 2-х строк варианта а) (см. в конце предыдущей главы). Например: небо и лес.
  2. Далее, программа создаёт, в случайном порядке, список-шаблонов, то-есть возможных строчек.
    Для первых 2-х строчек, когда стих начинается с “фона”, это:
    L1 = [ сущ, прилаг, глагол ]!,
    L2 = [ сущ, нареч, глагол ]!,
    L3 = [сущ, глагол]!,
    L = random(L1+L2+L3).
    Восклицательный знак, означает что этот список L будет состоят из всех возможных комбинаций данного малого списка.
    После создания всех 3-х списков: L1, L2, L3, они складываются в один большой список L, причём, элементы (малых списки) 3-х списков добавляются в общий список случайным образом.
    Для первых 2-х строчек, когда стих начинается с “предметов”, это:
    L1 = [ доп, сущ, нареч, глагол ]!,
    L2 = [ доп, сущ, прилаг, глагол ]!,
    L3 = [ сущ, доп, глагол ]!,
    L = random(L1+L2+L3).
    Для последующих 2-х строк, это:
    L1 = [ доп, сущ, нареч, глагол ]!,
    L2 = [ доп, сущ, прилаг, глагол ]!,
    L3 = [ прилаг, сущ, нареч, глагол ]!,
    L4 = [ сущ, прилаг, глагол ]!,
    L5 = [ сущ, нареч, глагол ]!,
    L6 = [ сущ, доп, глагол ]!,
    L7 = L1+L2+L3+L4+L5+L6,
    L8 = addСоюз(L7),
    L9 = [ сущ, глагол ]!,
    L = random(L7+L8+L9).
    Список L8 представляет собой сложение предыдущих списков, с добавлением к каждому подсписку, вначале, элемента “союз”, который при формировании строки будет заменятся одним из 3-х союзов: а, и, да (эти союзы особой смысловой нагрузки не несут и нужны только для того, чтобы строки с ямбом превратить в хорей - это участит случаи подборки строк для рифмовки, что увеличит разнообразие стихов, кроме того, помогают строкам быть хоть как-то связанными между собой, не связанным никак, разве что общей темой).
  3. Сочинение первых 2-х строк, если это о “фоне”, проходит следующим образом:
    a) берётся список форм, для 1-го существительного-объекта, в единственном числе и именительном падеже в случайном порядке (например: [лес]). И получая по очереди каждую форму из списка, начинает с первой формы в начале списка приступая к пункту b);
    b) прежде, чем приступить к прилагательному, сначала чистится список шаблонов от: если существительное не подходит под хорей, тогда удаляются строки начинающиеся на существительное ([ сущ | _ ]);
    c) по существительному в именительном падеже и един. числе находится список прилагательных в ед. числе и мужск. роде, связанных с ним. Используя род и число существительного, создаётся список прилагательных в заданном числе и роде, и добавленных в список в случайном порядке. Получая по очереди каждое прилагательное из списка, начиная с начала списка, программа приступает к пункту d);
    d) в этом месте чистится список шаблонов от: если существительное с идущим за ним прилагательным не подходят под хорей в начале слова, тогда удаляются строки начинающиеся на существительное с идущим за ним прилагательным ([ сущ, прилаг | _ ]); если существительное с идущим за ним прилагательным и перед ними однослоговым союзом не подходят под хорей в начале слова, тогда удаляются соответствующие строки ([ союз, сущ, прилаг | _ ]); если прилагательное с идущим за ним существительным не подходят под хорей в начале слова, или если прилагательное с идущим за ним существительным и перед ними однослоговым союзом не подходят под хорей в начале слова, тогда удаляются соответствующие строки ([ прилаг, сущ | _ ]; [ союз, прилаг, сущ | _ ]); также проверяются на хорей одно прилагательное и с союзом, после чего, если нужно, удаляются соответствующие строки ([ прилаг | _ ]; [ союз, прилаг | _ ]);
    e) по существительному в именительном падеже и един. числе находится список глаголов в инфинитиве, связанных с ним. Используя число существительного, создаётся список глаголов в заданном числе и настоящем времени, и добавленных в список в случайном порядке. И получая по очереди каждый глагол из списка, начиная с начала списка, программа приступает к пункту f);
    f) в этом месте чистится список-шаблонов также как и в предыдущем случае, но: с тремя параметрами: сущ., прилаг. и глагол; 2 раза с двумя параметрами: сущ. и глагол; прилаг. и глагол; и с одним параметром (глагол);
    g) по существительному в именительном падеже и един. числе, а также глаголе в инфинитиве находится список наречий (например: сущ_гл_нареч( небо, гореть, [ сущ( пламень, s ), вновь, слепяще, ослепляюще, радостно ] )), связанных с ними, которые программа преобразует в правильную форму и перемешивая данный список в случайном порядке создаёт список наречий. Затем получая по очереди каждое наречие из списка, начиная с начала списка, программа приступает к пункту h);
    h) в этом месте чистится список шаблонов также, как и в пункте f), но: с добавлением наречия;
    i) теперь, в полученном списке шаблонов, для первого сущ-объекта, все шаблонные имена (сущ, прилаг, глагол, наречие), программа заменяет на настоящие (например: [ глагол, сущ ] → [ поднимается, рассвет ]. После проверят, чтобы количество слогов (то-есть гласных) соответствовало заданному (8 или 7), иначе эта строка выбрасывается из списка;
    j) если после всех чисток списка шаблонов ничего не осталось, то методом отката ищутся другие варианты, иначе всё вышеперечисленное делается для второго сущ-объекта;
    k) когда всё вышеперечисленное (включая пункт j)) проделано с успехом, наступает время для рифмовки найденных строк. Из двух списков с найденными строками берутся первые строки (при новой попытке берётся следующая строка из списка - методом отката) и проверяется рифмуются ли их окончания так, как я описывал выше (см. 2-ую главу про рифмовку)? Если “да”, тогда эти строки выводятся на экран.
  4. Сочинение первых 2-х строк, если стих начинается с предметов, проходит таким же образом, как и при “фоне”, но с добавлением некоторых элементов:
    a) Теперь, список форм, как для 1-го сущ-го, так и для 2-го, образуется вместе с формами множественного числа, в перемешанном виде, где также форма берётся по порядку начиная с первого элемента списка;
    b) После получения наречия, из списка наречий (пункт g)), и чистки списка шаблонов новым параметром (наречие), по существительному в именительном падеже и един. числе, а также глаголе в инфинитиве находится список дополнений (в необработанном виде и без прилагательных, соответственно), при помощи которого образуется список с сущ-дополнениями и прилагательными для них (сущ-дополнений). Каждая ячейка, данного списка, содержит запись с четырьмя полямим: 1) предлог; 2) прилагательное; 3) существительное; 4) символ (a - только сущ.; b - сначала прилагательное; c - сначала существительное), причём сами слова, данных записей, уже преобразованы в нужный падеж и число. Пример:
    доп(sd(предл(в),прилаг(синем),сущ(море),b),1,[1,0,1,0]),
    Последние два аргумента - это номер ударной гласной в последнем слове, и ритмический рисунок всего дополнения в виде списка из нулей и единиц;
    c) Далее, чистится 4-ый раз список шаблонов, по тому же принципу, что и в предыдущих случаях.
    Дальше идёт рифмовка и прочее - всё как при сочинении “фона”.
  5. Что касается сочинения после 2-х первых строчек, оно происходит точно также, как и сочинение первых 2-х строчек о предметах (а не “фоне”), но без прилагательных у существительных-дополнений (это потому, что нельзя, скажем, вначале сказать “замёрзшее море”, а потом “у тёплого моря”) и с возможностью добавления союза в начале строки.
  6. В программе используются, по преимуществу, непереходные глаголы, и, следовательно, непрямое дополнение. Это потому, чтобы при сочинении строки, строка эта могла быть сочинена и без дополнения. А те глаголы, которые являются переходными, являются и непереходными в одном лице, так что, если при сочинении строки от дополнения придётся отказаться, то в этом случае глагол безболезненно превращается в непереходный. Например:
    а) Дождик капает на крышу;
    б) Тихий дождик капает.

4. О программе
Программа, для сочинения стихов, написано мною на прологе.
Стихи сочиняются от 30-40 минут и до несколько месяцев (на процессоре ≈ 3Ггц). Сочиняются строго 8 строчек. Слова берутся случайным образом, поэтому скорость сочинения зависит, так скажем, “от удачи”.
Поскольку, стихи сочиняются очень долго, для удобства, рекомендую использовать приложение PrologClassic версии не ниже 2.41 позволяющая работать программе даже при выключенном экране, а также скрывать окно программы на время сочинения.
Посмотрев программу Кукла 1.0, вы наверное заметили много подобного, например:

concat(L1,L2,L3):-
  assertA (con(L1,L2,[])),
  retractA (con([H|L01],L02,L03)),
  assertZ (con(L01,L02,[H|L03])),
  false;
   
  retractA (con([],[H|L02],L03)),
  assertZ (con([],L02,[H|L03])),
  false;
  
  retractA (con([],[],L)),
  assertA (con(L,[])),
  retractA (con([H|L01],L02)),
  assertZ (con(L01,[H|L02])),
  false;
   
  retractA (con([],L3)).

Вместо:

concat([],L,L).
concat([X|L1],L2,[X|L3]):-
  concat(L1,L2,L3).

Такой плохо читабельный код используется потому, что действия внутри правила выполняются с использованием отката, что не даёт исчерпать всю ОЗУ из-за её большой ограниченности (512мб; программа работает с большими данными). Такой подход не даёт расти стэку программы в памяти, вследствие чего программа и быстрее работает.
Хочу ещё сказать про программу, что часто встречается подобное:

assertA(ran(L,[])),
retractA(ran([H|L1],L0)),
добавитьВсписок(H,L0,L01),  
assertZ(ran(L1,L01)),

Здесь предикатом assertA(ran(L,[])) записываются некоторые данные фактом. Далее предикатами: retractA(ran([H|L1],L0)); assertZ(ran(L1,L01)), этот факт переписывается: сначала предикатом retractA удаляется сверху, потом предикатом assertZ добавляется снизу. При откате, не смотрят на то, что факт был один, из-за того что тот же факт был добавлен снизу, предикат retractA находит новое решение. Такой цикл повторяется до тех пор, пока retractA не сможет дать новое решение, из-за того, что первый аргумент факта ran является пустым списком.

Приложение1
Ещё стихи


Небо пламенем горит,
Море золотом блестит.
Лес тихонечко вздыхает,
Воздух ласково ласкает.
Воды глубоко блестят,
Грозы дикие гремят.
Мягкая волна уходит,
И луна по небу ходит.
время: 1 сутки 17 часов
процессор: 2,85Ггц.
дата: 18.02.2023


Небо вдалеке мерцает,
Море золотом сверкает.
Полная луна горит,
А кораблик бороздит.
Носиком дельфин махает,
Голубой рассвет светает.
На волне русалка спит,
Реже солнышко блестит.
время: 2 суток 17 часов
процессор: 2,85Ггц.
дата: 09.03.2023


Лес весною зеленеет,
Небо вдалеке темнеет.
Долго ёжики кружат,
Пчёлы быстрые жужжат.
Пчёлки кружатся лесные,
Вянут травки полевые.
Птица на небе парит,
Долго бабочка кружит.
время: 11 часов;
процессор: 2,85Ггц;
дата: 02.02.2023.


Аист в небесах летит,
Заяц к зайчикам бежит.
Воздух влажный замирает,
Да лесной ручей сверкает.
К другу храбрый лис спешит,
Быстро облако бежит.
Ветер по полям гуляет,
Ёжик по лесам шагает.
время: 12 часов 45 минут;
процессор: 2,85Ггц.
дата: 13.02.2023


Ветер в ухе злой свистит,
Око на свету блестит.
Да лучистый блеск сияет,
Страсть пьяняще опьяняет.
Сладкая дрожит губа,
Юная цветёт весна.
Дождик с неба поливает,
Грусть весьма оледеняет.
время: 4 часа 30 минут
процессор: 2,85Ггц
дата: 10.05.2023


Высоко луна летает,
Нежно дева запевает.
Очи на свету блестят,
Души горестно скорбят.
В сердце грусть тоскою ноет,
Страсть у сердца тихо воет.
Капли на губе дрожат,
Пламенем сердца горят.
время: 6 часов
процессор: 2,85Ггц
дата: 11.05.2023

Приложение2
Программа Кукла 0.0


маленькие_зайцы_тихо_бегут
быстро_бегут_муравьи
белые_лебеди_тихо_плывут
долго_бьются_цари


злой_ветер_снова_свистит
тихо_спят_моря
тёмная_вода_громко_кипит
жарко_пылает_заря


зимнее_небо_вновь_горит
пухом_летят_облака
яркий_лес_стеною_стоит
вновь_горит_звезда

Эта программа тоже сочиняет стихи, но намного упрощённее.
Суть её в том, чтобы создать четырёхстишие, где:

  1. рифмовка всегда перекрёстная [см. системы рифмовки на 2], то-есть рифмуются 1 и 3 строки, а также 2 и 4 строки;
  2. при рифмовке окончаний строк проверяется:
    • чтобы ударные гласные были одинаковые или парные. «Парными» являются а и я, е и э, и и ы, о и ё, у и ю;
    • чтобы кол-во безударных гласных после последней ударной было одинаково;
    • чтобы окончание после ударной гласной было либо у обоих строк нулевым, либо у обоих не нулевым.
    Данная рифма принадлежит к «бедной» рифме, то-есть - неточная;
  3. структура стиха строго фиксирована:
    • для 1 и 3 строчек это - Прилаг Сущ Нареч Глагол;
    • для 2 и 4 строчек - Нареч Глагол Сущ.
  4. используется чисто тоническая система стихосложения [см. тоническая система стиха 2]. То-есть рифмованные строки должны иметь только равное количество ударений, без определённого их расположения. А поскольку, слов в рифмованных строках всегда равное количество, то и ударений всегда будет равное количество. И проверять это в программе, соответственно, не нужно;
  5. первая гласная в начале и последняя в конце строки всегда должны быть ударными. На мой слух это делает стихотворение интонационно детским;
  6. в строках 1 и 3 в последних двух словах не должно быть подряд идущих 3-х безударных гласных, а в строках 2 и 4 - по всей длине. На мой слух это портит стих.
    Создаётся стих программой от 10 минут и до пару часов (большая база знаний) на процессоре в 3Ггц.
    Разберём программу.
    Целевой предикат:
    ?-
    write(загрузка_файла_наречий…), nl,
    see_file(‘/storage/emulated/0/Download/Стихи1/Нареч.txt’),
    write(загрузка_файла_прилагательных…), nl,
    see_file(‘/storage/emulated/0/Download/Стихи1/Прилаг.txt’),
    write(загрузка_файла_существительных…), nl,
    see_file(‘/storage/emulated/0/Download/Стихи1/Сущ.txt’),
    write(загрузка_файла_глаголов…), nl,
    see_file(‘/storage/emulated/0/Download/Стихи1/Глагол.txt’),
    write(загрузка_файла_словосочетаний…), nl,
    see_file(‘/storage/emulated/0/Download/Стихи1/СловСоч.txt’),
    false;
    nl, write(ждите…), nl,nl,
    сочинитьЧетвер,
    nl.

    Состоит из двух вопросов, где они разделяются оператором ‘;’:
    a) В первом вопросе, с помощью предиката see_file загружаются различные базы знаний.
    see_file(F):-
    see(F),
    assertA(n(0)), %Для подсчёта
    readAll, !;
    write(фаил_отсуствует.), nl,
    false.

    С помощью предиката readAll мы считываем из файла по одному предикату и записываем в оперативную память нашей программы.
    Обратите внимание на предикат
    share(N):- N mod 2000 = 0.
    Он нужен для того, чтобы после считывания 2000 строк откатом освободить стэк в оперативной памяти программы и затем считывать дальше. Если мы этого делать не будем, то в скором времени приложение вылетит с ошибкой нехватки памяти, из-за того что, андроид пока что предоставляет java-приложениям только 512мб оперативной памяти.
    В конце первого вопроса стоит ложный предикат false. С его помощью мы освобождаем стэк перед выполнением следующего вопроса.
    b) Во втором вопросе предикат сочинитьЧетвер и выполняет работу по созданию четырёхстишия. Этот предикат состоит из четырёх частей, где каждая часть - это сочинение одной строки, а после сочинения двух рифмующихся строк, сразу проверяется наличие между ними рифмовки, в противном случае подбирается другая парная вторая строка и так до тех пор, пока строки не будут рифмоваться.
    l) Рассмотрим первую часть.
    getСущ1(Ss,сущ1),
    сущ00(Ns,Ss),
    getСущ_глагол1(Ss,Gsi,сущ_глагол1),
    сущ_гл_нареч(Ss,Gsi,),
    сущ0(Ss,SsL,Sr,SplL),
    глагол0(Gsi,Gs,Ga2s,Gpl,Ga2pl),
    plural(SsL,SplL,
    Gs,Ga2s,
    Gpl,Ga2pl,plural1,
    S,Sa2,G,Ga2),
    vowels(S,Sa1),
    vowels(G,Ga1),
    num_rifm(Ga1,Ga2,L13),
    reverse(L13,[1|
    ]),
    getСущ_прилаг1(Ss,Ps,сущ_прилаг1),
    прилаг0(Ps,Pa2),
    Ss = сущ(S_0,),
    прилаг_чсл_род(S,S_0,Sr,Ps,P),
    vowels(P,Pa1),
    num_rifm(Pa1,Pa2,L10),
    L10 = [1|
    ],
    getСущ_гл_нареч1(Ss,Gsi,Ni,нареч_глагол1),
    нареч0(Ni,Na2,N),
    vowels(N,Na1),
    num_rifm(Na1,Na2,L12),
    concat(L12,L13,L14),
    step2(L14),
    E = Ga1-Ga2,
    string(St,[P,‘‘,S,’’,N,‘_’,G]),
    write(St), nl, !,

    Предикатом getСущ1(Ss,сущ1) мы, сначала, создаём список случайно расположенных в нём существительных из тех что есть в файле СловСоч.txt. После чего он «по порядку» выдаёт нам каждое из них. «По порядку» - значит с каждым новым выполнением после отката. Второй аргумент здесь нужен для метки списка существительных к которому мы обращаемся (он сохраняется фактом, а не в стэке - для оптимизации программы; нам предоставлено всего 512мб).
    Также предикатом getСущ_глагол1(Ss,Gsi,сущ_глагол1) мы получаем случайный глагол связанный с этим с существительным Ss (см. коментарии getСущ1, выше).
    Эта связь в базе знаний выглядит так:
    сущ_глагол( рыбка, [ плескаться, играть, резвиться ] ).
    Нужно иметь такую связь для того, чтобы не брать неподходящие абсурдные глаголы, например для рыбки: шуршит, плюётся, рубит и т.д.
    Предикат сущ_гл_нареч(Ss,Gsi,_) используется для оптимизации. Так как первая строка должна иметь наречие, а оно у глаголов связанных с существительным часто отсутствует, то, чтобы, лишний раз, долго не перебирать после прилагательные и прочее, сразу проверяет есть ли смысл работать дальше с данным глаголом при существительном Ss.
    Из предиката сущ0(Ss,SsL,Sr,SplL), с помощью порядкового номера существительного находящегося в терме переменной Ss, получаем списки форм существительного в единственном числе и во множественном (именительный падеж; не могу вспомнить встречал ли существительное где единственном числе или во множественном именительного падежа бывает несколько форм, но на всякий случай пускай будет). Переменная Sr - это род существительного которое мы получаем.
    Из предиката глагол0(Gsi,Gs,Ga2s,Gpl,Ga2pl), вводя глагол в инфинитиве Gsi, мы получаем единственное и множественное число настоящего времени, а также порядковый номер ударной гласной.
    глагол0(W0,W1,A1,W2,A2):-
    глагол_наст(W0,W1,A1,W2,A2), !.

    Как видно предикат глагол0 - это всего лишь поиск факта глагол_наст с теми же аргументами. Но в конце имеет отсечение предотвращающий дальнейший безуспешный поиск.
    Предикат plural(SsL,SplL, Gs,Ga2s, Gpl,Ga2pl,plural1, S,Sa2,G,Ga2) выдаёт случайным образом либо существительное и глагол в единственном числе, либо - во множественном, вместе с порядковыми номерами ударных гласных.
    Предикатом vowels(S,Sa1) получаем кол-во гласных в слове. Это нам понадобится для составления рисунка ритма (в виде списка нулей и единиц; 0 - безударная гласная, 1 - ударная).
    Предикат num_rifm(Ga1,Ga2,L13) составляет рисунок ритма, в данном случае для глагола. А с помощью предиката reverse(L13,[1|_]) проверяем в перевёрнутом списке L13, чтобы последняя гласная в строке была ударной (см. начало рассказа про данную программу), так как на этот глагол заканчивается строка.
    Предикат getСущ_прилаг1(Ss,Ps,сущ_прилаг1) выдаёт случайное прилагательное связанное с существительным Ss (см. комментарии getСущ1, выше).
    прилаг0(Ps,Pa2) - получаем номер ударной гласной прилагательного (оно будет таким же и после изменения прилагательного по роду).
    прилаг_чсл_род(S,Ss,Sr,Ps,P) - переменная Ss - это существительное в единственном числе, а S - это текущее существительное. Если эти две переменные равны тогда прилагательное Ps изменяется по роду по схеме единственного числа, в противном - по схеме множественного числа.
    L10 = [1|_] - утверждает что первая гласная в строке должна быть ударной.
    getСущ_гл_нареч1(Ss,Gsi,Ni,нареч_глагол1) выдаёт случайное наречие связанное с существительным Ss и глаголом Gsi. Ni - является ещё не обработанным наречием, оно может содержать конструкцию, например сущ( круг, 670, s ), где третий аргумент это число существительного.
    нареч0(Ni,Na2,N) - выдаёт номер ударной гласной и преобразованное наречие.
    Предикат concat соединяет списки в один.
    step2(L14) - проверяет, чтобы в списке последних двух слов небыло трёх гласных безударных подряд.
    E = Ga1-Ga2 - выдаёт кол-во гласных в окончании.
    string(St,[P,‘‘,S,’’,N,‘_’,G]) - соединяет слова в строку;
    ll) Рассмотрим в третей части те предикаты которых нет в 1 части.
    not (G = G3) - конечные слова, в рифмующихся строках, не должны быть одинаковыми.
    пара(B1,B3) - проверяет рифмующиеся ударные гласные, чтобы были одинаковыми или парными.
    E = G3a1-G3a2 - проверяет окончания строк после ударной гласной, чтобы кол-во гласных было одинаково.
    lll) Вторую и четвёртую части, думаю, сами сможете разобрать - они похожи на первую и третью.

У программы есть один минус. Она так написана, что если в 1-ой или 2-ой строке стиха получится окончение такое, что более нигде такого не найти, то отката этой строки не будет и далее программа завершит работу.

Ещё стишки


тоненькая_русалка_тихо_поёт
весело_дуют_ветра
пенистая_вода_плавно_течёт
вновь_уходит_гроза


дивные_звёзды_слепяще_горят
всюду_бродят_ветра
серые_чайки_ввысь_летят
вновь_горят_небеса


серая_чайка_громко_кричит
низко_бегут_облака
маленькое_судно_тихо_бежит
тихо_дремлют_леса


жёлтый_жираф_громко_спит
весело_шумят_ветра
проворный_поток_быстро_бежит
быстро_бегут_суда

Статью написал Елевферий elevferii_pechori@mail.ru.

Программы по стихам и базы знаний для них находятся здесь: https://disk.yandex.ru/d/XXPaMKVUMLMRIg.
Папку «Кукла_1_0» с файлами нужно скопировать в папку Download внутренней памяти устройства.

Приложение скачать можно здесь: https://disk.yandex.ru/d/5ED-43Uveub1ew.

Список литературы

  1. Исполнение Пролог-программ на Android. URL: https://pro-prof.com/forums/topic/исполнение-пролог-программ-на-android#post-8560;
  2. Учебник стихосложения, URL: https://stihi.ru/uchebnik/;
  3. Генератор поздравительных стихов, URL: https://almiur.ru/generator_output.php;
  4. “Как научить нейросеть генерировать стихи” - Илья Гусев, URL: https://m.youtube.com/watch?v=wTN-qKPu4c0&feature=youtu.be;
  5. Поэтика, Методическое пособие по стихосложению, Воронеж 2005.

Скорбь о былом счастье

Сердцу голос напевает,
В небе ласточка порхает.
Блеск лучами веселит,
Редкая слеза блестит.
Ветер шелестит листвою,
Терема дымят трубою.
Тяжело скорбит душа,
Дрогнет на лице губа.

время: 5 часов
процессор: 2,85Ггц
дата: 24.05.2023

Воспоминание о расставании

Мягко серебрит луна,
Плачет дева у окна.
Тусклый свет едва мерцает,
Страсть сердечко вновь снедает.
Алая горит щека,
Катится с щеки слеза.
Теремок дымит трубою,
Сердце грусть щемит тоскою.

время: 21 час
процессор: 2,85Ггц
дата: 05.06.2023

  1. Я хотел бы использовать Debian 12 (bookworm) для запуска пролога, что мне делать?
    (ладно, вобщем-то не особенно-то и хотел, иначе бы сам разобрался);
  2. Сколько денег стоит гонять 3-х гигагерцовый комп (жручий, наверное, как утюг - киловаттами) сутками (при текущих тарифах несколько рублей за киловатт-час)? Сколько стоит один стих в переменных издержках и в постоянных (с учётом стоимости амортизации аппаратуры);
  3. Как зарабатывать на этих стихах, чтобы окупить затраты из пункта 2? (И заодно оплатить время компьютерно-аугментированного человека, которого надо нанять в качестве автора стихов, налоги, и оставить фонд компании на развитие).
  4. Можно ли повысить скорость работы используя видеоускорители или ускорители вычислений? (отбив их у майнеров криптовалюты)

Я прочитал русскую Пролог (язык программирования) — Википедия и английскую Prolog - Wikipedia страницы про пролог. Однако не нашел там слова PrologClassic.

Когда я спросил у Chat GPT, в чём разница между PrologClassic и SWI-Prolog, он ответил мне, что первая программа закрытая и коммерческая, а вторая опенсорсная (что лучше, конечно). Верны ли эти данные?

Интернет пишет
«один из читателей нашего форума — Елевферий Васильевич Миронов, который разработал интерпретатор языка Пролог, работающий на Android и поделился им с миром.»

То есть, Вы и есть автор (предположительно). В статье на которую вы ссылаетесь написано что “написано на Java n-ide для Андроид (написанное mr Duy)”.
Я не вчитывался сильно, искал лицензию и ссылку на исходные тексты (чтобы выяснить опенсорс или нет). Не нашел.

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

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

Исходник я не выкладывал.

Именно об этом я и написал.

читайте внимательно, то что я вам пишу

Всё верно, яндекс-диск - это недоверенная локация, никто авторитетный не поручается своими деньгами и репутацией за её содержимое.

Такие посты не нужно там писать

Это обсуждение модерирования (не знаю, запрещено ли оно на этом форуме).

В лесу
Небо заревом поёт,
Лес цветочками цветёт.
Тихо бабочка летает,
Ветерок вдали гуляет.
У тропы ручей бежит,
Пчёлка радостно жужжит.
Теремки дымят трубою,
Ласточка летит стрелою.
время: 1 сутки 40 минут;
процессор: 2,85Ггц
дата: 18.07.2023

В бурном море
Тучи ходят в небесах,
Скачют судна на ветрах.
С волнами дельфин играет,
Над морем рассвет светает.
Сильно молнии гремят,
Волны мягкие шумят.
Ветер свищет над водою,
Чайки кружат над волною.
время: 1 сутки 2 часа 15 минут
процессор: 2,85Ггц
дата: 26.08.2023

Теперь можно скачать с NashStore - проверено на вредительство.)

Хорошее достижение, которое заслуживает уважения. Скачивать, конечно, я всё равно не буду :)

В море Пушкина
Солнце воду золотит,
У камней русалка спит.
Воздух тихо замирает,
Рыбка под водой играет.
Воют вдалеке ветра,
Тают в небе облака.
Аист по небу кружится,
И корабль на волнах мчится.
время: 16 суток и 42 минуты
процессор: 2,85Ггц
дата: 28.10.2023

Можно эти компьютерные стихи перефразировать из парной рифмы в перекрёстную. Тогда эти стихи приобретают более интересный «настрой».
Например, «В море Пушкина»:
Воздух тихо замирает,
Солнце воду золотит.
Рыбка под водой играет,
У камней русалка спит.

Аист по небу кружится,
Воют вдалеке ветра.
Наш корабль по волнам мчится,
Тают в небе облака.

Или «В бурном море»:
Над морем рассвет светает,
Тучи ходят в небесах.
С волнами дельфин играет,
Скачют судна на ветрах.

Ветер свищет над водою,
В небе молнии гремят.
Кружат чайки над водою,
Волны мягкие шумят.

16 суток рантайма это дорого. 24 часа / сутках * 16 суток * 1 киловатт*час * 6,43 руб / (киловатт*час) = 2469.12 руб
Плюс амортизация оборудования:
Базовая сборка = 79 500 руб / ( 3 года * 365 (дней/году) * 24 (часа/в сутках)) * 16 * 24 = 3287.67 руб
Плюс аренда помещения, Средн. цена м2 / мес = 2406
16 суток = 2406 / 30 * 16 = 1283.2 руб
Итого себестоимость стиха «В море Пушкина» ≥ 7039.99 руб
МРОТ (Москва) = 24801 руб/мес.
Этот стих за эти деньги мог бы писать человек в течение 3-х рабочих дней на фуллтайм.

Память о море
Небо глубоко вздыхает,
Пенится в морях волна.
Море заревом пылает,
Собирается гроза.

Свет рассветы пробуждает,
Звёзды первые блестят.
Под водой дельфин ныряет,
Да кораблики летят.

Это перефраз стиха:
Небо глубоко вздыхает,
Море заревом пылает.
Пенится в морях волна,
Собирается гроза.

Свет рассветы пробуждает,
Под водой дельфин ныряет.
Звёзды первые блестят,
Да кораблики летят.
время: 6 часов и 20 минут
процессор: 3,1Ггц
дата: 27.04.2024
программа: Кукла 1.1alfa

Вам надо почитать психологию и философию (или на ютубе посмотреть лекции МГУ). Такие категории как “личность”, “индивидуальность”, “сознание”, “смысл”.
Тогда Вам должно стать понятно, чем великие литературные произведения отличаются от того, что генерирует Ваша программа.

Ну вы загнули - “великие”. Я на это не претендую, да и нынешние чаты GPT далеки от этого.:grin:

Вы стараетесь передать эмоции? У Вас в программе есть модель эмоций? Как Вы собираетесь без психологии обойтись, не понимаю…

А это блестящая идея, только в каких книгах почитать про это?

Теперь, написана версия 1.1.
Версия программы 1.1 делает тоже самое, что и версия 1.0. Но, создаёт не по 2 строки за раз, а по 4 (2 строки параллельно с другими 2 строками), что ускоряет программу.
(О параллельном программировании читайте последний пост здесь)