Ответ в теме: Логическая задача. Проверка условий в Prolog

      Комментарии к записи Ответ в теме: Логическая задача. Проверка условий в Prolog отключены
#2937

В разделе domains описываем сначала типы перечислений – способы перемещения, цвета и имена, затем добавляем структуру персонажа. Гипотеза (то, что предполагает программа), в этом случае, представляет собой список персонажей. Кроме того, при выборе тех, кто одевает зеленое в нашей программе появится список имен, поэтому он тоже должен быть описан:

domains
  тип_способ_перемещения = pes; tax; tram
  тип_цвет_одежды = red; green; blue; orange
  тип_имя = ma; mb; mc; md
  
  тип_структура_зазнадец = zaz(тип_имя, тип_цвет_одежды, тип_способ_перемещения)
  
  тип_гипотеза = тип_структура_зазнадец*
  
  тип_имена = тип_имя*

В разделе predicates нужно описать все функции, которые нам потребуются:

predicates
  nondeterm способ_перемещения(тип_способ_перемещения)
  nondeterm цвет_одежды(тип_цвет_одежды)
  nondeterm имя(тип_имя)
  
  nondeterm генерация_гипотезы(тип_гипотеза)
  nondeterm проверка_гипотезы(тип_гипотеза)
  
  nondeterm входит_в(тип_структура_зазнадец, тип_гипотеза)
  nondeterm входит_в(тип_имя, список_имен)

  nondeterm есть_цвет_способ(тип_цвет_одежды, тип_способ_перемещения, тип_гипотеза)
  nondeterm есть_имя_способ(тип_имя, тип_способ_перемещения, тип_гипотеза)
  nondeterm есть_имя_цвет(тип_имя, тип_цвет_одежды, тип_гипотеза)
  
  nondeterm проверка_есть_пара_способ_передвижения(тип_гипотеза, тип_способ_перемещения)
  
  nondeterm проверка_имя_красное_ИЛИ_пешком(тип_имя, тип_гипотеза)

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

clauses
  имя(ma).
  имя(mb).
  имя(mc).
  имя(md).
  
  цвет_одежды(red).
  цвет_одежды(green).
  цвет_одежды(blue).
  цвет_одежды(orange).
  
  способ_перемещения(pes).
  способ_перемещения(tax).
  способ_перемещения(tram).

Предикат входит_в в этой программе полностью эквивалентен предикату member.

Генерация гипотезы сводится к построению списка из четырех элементов. Имена персонажей можно сразу фиксировать и запросить лишь генерацию цветов и способов перемещения:

  генерация_гипотезы([
    zaz(ma, ЦветА, СпособА),
    zaz(mb, ЦветБ, СпособБ),
    zaz(mc, ЦветВ, СпособВ),
    zaz(md, ЦветГ, СпособГ)
  ]):-
    цвет_одежды(ЦветА), цвет_одежды(ЦветБ), цвет_одежды(ЦветВ), цвет_одежды(ЦветГ),
    
    /*
    NOT(ЦветА = ЦветБ), NOT(ЦветА = ЦветВ), NOT(ЦветА = ЦветГ), 
    NOT(ЦветБ = ЦветВ), NOT(ЦветБ = ЦветГ),
    NOT(ЦветГ = ЦветВ),
    */
    
    способ_перемещения(СпособА), способ_перемещения(СпособБ), 
    способ_перемещения(СпособВ), способ_перемещения(СпособГ).

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

Следующие три предиката проверяют наличие в гипотезе человека с двумя из трех нужных параметров. При программировании на SWI Prolog, например, от них можно было бы избавиться, но в Visual Prolog оператор NOT не может применяться к запросам, содержащим анонимную переменную. Конструкция NOT(входит_в(zaz(ma, _Цвет, tax), Гипотеза)) привела бы к ошибке компиляции, т.к. мы передаем несвязанную переменную _Цвет оператору NOT. Для решения проблемы нужно написать предикаты, принимающие только два параметра и скрывающие лишнюю переменную от NOT:

есть_цвет_способ(Цвет, Способ, Гипотеза):-
  	входит_в(zaz(_Имя, Цвет, Способ), Гипотеза).
  	
  есть_имя_способ(Имя, Способ, Гипотеза):-
    	входит_в(zaz(Имя, _Цвет, Способ), Гипотеза).
  	
  есть_имя_цвет(Имя, Цвет, Гипотеза):-
   	входит_в(zaz(Имя, Цвет, _Способ), Гипотеза).

Следующие предикаты проверяют выполнение конкретных условий задачи:

проверка_есть_пара_способ_передвижения(Гипотеза, Способ):-
	входит_в(zaz(Имя1, _Цвет1, Способ), Гипотеза), 
	входит_в(zaz(Имя2, _Цвет2, Способ), Гипотеза),
	NOT(Имя1 = Имя2).

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

проверка_имя_красное_ИЛИ_пешком(Имя, Гипотеза):-
  	входит_в(zaz(Имя, _Цвет, pes), Гипотеза);
  	входит_в(zaz(Имя, red, _Способ), Гипотеза).

Функция принимает имя персонажа и проверяет что гипотеза (наше предположение) содержит либо этого персонажа в красном костюме, либо двигающегося пешком.

Проверка гипотезы собирает все описанные выше предикаты в одно высказывание чтобы обеспечить выполнение всех условий задачи:

  проверка_гипотезы(Гипотеза):-
  	NOT(проверка_есть_пара_способ_передвижения(Гипотеза, pes)),
  	NOT(проверка_есть_пара_способ_передвижения(Гипотеза, tax)),
  	
 	NOT(есть_имя_способ(ma, tax, Гипотеза)),
 	NOT(есть_имя_цвет(ma, red, Гипотеза)),
 	NOT(есть_имя_способ(mb, tax, Гипотеза)),
 	NOT(есть_имя_цвет(mb, red, Гипотеза)),
 	
 	NOT(есть_цвет_способ(red, pes, Гипотеза)),
 	
 	NOT(есть_цвет_способ(green, tax, Гипотеза)),
    	NOT(есть_цвет_способ(blue, tax, Гипотеза)),
 	
 	findall(Имя, входит_в(zaz(Имя, green, _Способ), Гипотеза), ОдеваютЗеленое),
  	ОдеваютЗеленое = [ЕдинственныйОдеваетЗеленое],
  	NOT(ЕдинственныйОдеваетЗеленое = mb),
  	NOT(есть_имя_способ(ЕдинственныйОдеваетЗеленое, tram, Гипотеза)),
 	
 	NOT(есть_имя_способ(mc, tram, Гипотеза)),
    	%NOT(есть_имя_цвет(mc, orange, Гипотеза)),
    	NOT(есть_имя_цвет(md, orange, Гипотеза)),
 	
	NOT(проверка_имя_красное_ИЛИ_пешком(md, Гипотеза)).

В секции goal остается лишь запустить генерацию гипотезы и последующую ее проверку:

goal
 генерация_гипотезы(Гипотеза),
 
 проверка_гипотезы(Гипотеза).

На скриншоте показан пример работы программы.