Есть ли способ расширить загадку Эйнштейна на Прологе?

Во-первых, спасибо, что заглянули на мой вопрос. Что касается вопроса, я работал над кодом Prolog для моделирования загадки Эйнштейна. Я думаю, что это довольно элегантное решение, в основном это список кортежей с определенными полями, такими как цвет дома, домашнее животное, национальность и т. д. У меня есть смоделированный список и подсказки, но у меня возникли проблемы с попыткой смоделировать вопрос.

Дело в том, что я не хотел ограничиваться простым вопросом «Кому владеет рыба?». Я хотел смоделировать более сложный вопрос. Например: какое домашнее животное есть у человека, который живет в доме, который справа от красного дома, владеет >?. Кажется, это простая проверка индекса i в списке, в котором есть кортеж с цветом red, и возвращение поля pet кортежа с индексом i+1 в списке, но мой опыт работы с Prolog довольно ограничен (это практически впервые использую его), и я не совсем уверен, как это реализовать. Я предоставлю код, который у меня есть ниже. Я ценю все ответы. Спасибо.

persoane(0, []) :- ! .
persoane(N, [(_Men, _Color, _Drink, _Smoke, _Animal)|T]) :- N1 is N-1, persoane(N1,T) .

persoana(1, [H|_], H) :- ! .
persoana(N, [_|T], R) :- N1 is N-1, persoana(N1, T, R)

% Function that tests if A is to the right of B (will be used in the final question)
right(A, B, List) :- nextto(B, A, List).

% The Brit lives in the red house
hint1([(brit, red, _, _, _)|_]) .
hint1([_|T]) :- hint1(T) .

% The Swede keeps dogs as pets
hint2([(swede, _, _, _, dog)|_]) .
hint2([_|T]) :- hint2(T) .

% The Dane drinks tea
hint3([(dane, _, tea, _, _)|_]) .
hint3([_|T]) :- hint3(T) .

% The green house is on the left of the white house
hint4([(_, green, _, _, _,),(_, white, _, _, _)|_]) .
hint4([_|T]) :- hint4(T) .

% The green house owner drinks coffee
hint5([(_, green, cofee, _, _)|_]) .
hint5([_|T]) :- hint5(T) .

% The person who smokes Pall Mall rears birds
hint6([(_, _, _, pallmall, bird)|_]) .
hint6([_|T]) :- hint6(T) .

% The owner of the yellow house smokes Dunhill
hint7([(_, yellow, _, dunhill, _)|_]) .
hint7([_|T]) :- hint7(T) .

% The man living in the center house drinks milk
hint8(persoane) :- persoana(3, persoane, (_, _, milk, _, _)) .

% The Norwegian lives in the first house
hint9(persoane) :- persoana(1, persoane, (norwegian, _, _, _, _)) .

% The man who smokes Blends lives next to the one who keeps cats
hint10([(_, _, _, blend, _),(_, _, _, _, cat)|_]) .
hint10([(_, _, _, _, cat),(_, _, _, blend, _)|_]) .
hint10([_|T]) :- hint10(T) .

% The man who keeps horses lives next to the man who smokes Dunhill
hint11([(_, _, _, dunhill, _),(_, _, _, _, horse)|_]) .
hint11([(_, _, _, _, horse),(_, _, _, dunhill, _)|_]) .
hint11([_|T]) :- hint11(T) .

% The owner who smokes BlueMaster drinks beer
hint12([(_, _, beer, bluemaster, _)|_]) .
hint12([_|T]) :- hint12(T) .

% The German smokes Prince
hint13([(german, _, _, prince, _)|_]) .
hint13([_|T]) :- hint13(T) .

% The Norwegian lives next to the blue house
hint14([(norwegian, _, _, _, _),(_, blue, _, _, _)|_]) .
hint14([(_, blue, _, _, _),(norwegian, _, _, _, _)|_]) .
hint14([_|T]) :- hint14(T) .

% The man who smokes Blend has a neighbour who drinks water
hint15([(_, _, _, blend, _),(_, _, water, _, _)|_]) .
hint15([(_, _, water, _, _),(_, _, _, blend, _)|_]) .
hint15([_|T]) :- hint15(T) .

% The question: What pet does the man who lives to the right of the red house have ?
% question (right((_, _, _, _, _), (_, red, _, _, _), persoane)) .
question([(_, red, _, _, _),()])
question([_|T]) :- question(T) .

solution(persoane) :-
   persoana(5, persoane),
   hint1(persoane),
   hint2(persoane),
   hint3(persoane),
   hint4(persoane),
   hint5(persoane),
   hint6(persoane),
   hint7(persoane),
   hint8(persoane),
   hint9(persoane),
   hint10(persoane),
   hint11(persoane),
   hint12(persoane),
   hint13(persoane),
   hint14(persoane),
   hint15(persoane),
   question(persoane) .


person Hachiko1337    schedule 23.03.2020    source источник
comment
Каких результатов вы ожидали от таких запросов, как persoane(2,X)?   -  person Peter Ludemann    schedule 23.03.2020
comment
(1,2,3,4,5) не является кортежем. Это сокращение от ','(1,','(2,','(3,','(4,5)))). (Используйте write_canonical/1, чтобы увидеть это). Я думаю, вы хотите использовать обычные списки, например. [1,2,3,4,5]. В вашей системе может быть предикат nth0/3 или nth1/3 --- посмотрите, как он реализован.   -  person Peter Ludemann    schedule 23.03.2020
comment
Я почти уверен, что persoane(2,X) просто вернет информацию о кортеже с индексом 2, что-то вроде (датчанин, синий, чай, смесь, лошадь), поскольку это второй дом в решении загадки Эйнштейна. Я думаю, это совершенно нормально, если из таких кортежей нельзя извлечь конкретную информацию, например, о домашнем животном или цвете дома. Я обратился к Stackoverflow, потому что у меня не осталось идей, как с этим справиться. Я помню, как проверял такие вещи, как проблема с зеброй, но большинство из них не касались более сложных вопросов, таких как тот, который я упомянул в вопросе.   -  person Hachiko1337    schedule 23.03.2020
comment
Я рассмотрю ваши предложения, возможно, я смогу найти что-то.   -  person Hachiko1337    schedule 23.03.2020
comment
Мне удалось кое-что сделать с этой загадкой, код можно найти здесь ссылка. Интересно, есть ли способ запросить эти данные, чтобы вывести домашнее животное владельца дома справа от красного дома.   -  person Hachiko1337    schedule 23.03.2020
comment
В Прологе переменные начинаются с заглавной буквы. Попробуйте изменить свой последний предикат на: ``` решение(Личное) :- persoana(5, Личное), hint1(Личное), hint2(Личное), ... hint15(Личное), question(Личное) . ```   -  person Peter Ludemann    schedule 24.03.2020
comment
Это хорошо известная проблема. Попробуйте выполнить поиск по проблеме с зеброй, чтобы найти много других вопросов и ответов.   -  person Peter Ludemann    schedule 24.03.2020


Ответы (2)


Вы можете получить позиционный доступ к списку следующим образом:

index(one,   [One, _, _, _, _],   One).
index(two,   [_, Two, _, _, _],   Two).
index(three, [_, _, Three, _, _], Three).
index(four,  [_, _, _, Four, _],  Four).
index(five(, [_, _, _, _, Five],  Five).

Вы также можете определить next_to аналогичным образом:

next_to(List, A, B) :- left_right(List, A, B).
next_to(List, A, B) :- right_left(List, A, B).
right_left(List, A, B) :- left_right(List, B, A).
left_right([A,B,_,_,_], A,B).
left_right([_,A,B,_,_], A,B).
left_right([_,_,A,B,_], A,B).
left_right([_,_,_,A,B], A,B).

Существуют более общие решения, которые работают для любой длины списка (оставлено в качестве упражнения).

person Peter Ludemann    schedule 23.03.2020

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

puzzle( P, Pet ) :- length( P, _ ),
    clue( P, [house-red , nation-brit ] ),    % (* 1. The Brit lives in red house *)
    clue( P, [keeps-dogs, nation-swede] ),    % (* 2. The Swede has dogs as pets  *)
    clue( P, [drinks-tea, nation-dane ] ),                      % (* 3. .....  *)
    clue( P,  left_of, [[house -red   ], [house -white  ]] ),   % (* 4. .....  *)
    clue( P, [house-green,  drinks-coffee  ] ),                 % (*  .......  *)
    clue( P, [keeps-birds,  smokes-pallmall] ),                   
    clue( P, [house-yellow, smokes-dunhill ] ),                   
    clue( P,  center,  [[drinks-milk  ]                  ] ),     
    clue( P,  first,   [[nation-norse ]                  ] ),     
    clue( P,  next_to, [[smokes-blends], [keeps -cats   ]] ),     
    clue( P,  next_to, [[keeps -horses], [smokes-dunhill]] ),     
    clue( P, [smokes-bluemaster, drinks-beer  ] ),                
    clue( P, [nation-german    , smokes-prince] ),
    clue( P,  next_to, [[nation-norse ], [house -blue   ]] ),
    clue( P,  next_to, [[smokes-blends], [drinks-water  ]] ),
    %% (* Who has the zebra ? *)
    clue( P, [keeps-zebra, nation- _Who] ),
    %% (* What pet the man owns who lives to the right of the red house? *)
    clue( P, right_of, [[keeps-Pet   ], [house -red    ]] ),
    maplist( length, P, _ ).

clue( P, AS ) :- member( H, P ), attrs( H, AS ).
clue( P, C, ASs ) :- G =.. [ C, P | ASs], G.

left_of(  P, AS1, AS2 ) :- append( _, [H1, H2 | _], P ),
                                           attrs( H1, AS1 ),
                                           attrs( H2, AS2 ).
next_to(  P, AS1, AS2 ) :- left_of( P, AS1, AS2 ) ; right_of( P, AS1, AS2 ).
right_of( P, AS1, AS2 ) :- left_of( P, AS2, AS1 ).
center(   P,      AS  ) :- middle( P, H ), attrs( H , AS  ).
first(  [H | _],  AS  ) :-                 attrs( H , AS  ).
middle( [H    ], H ).
middle( [_ | R], H ) :- append( X, [_], R ), middle( X, H ).

Осталось определить attrs/2, реализуя расширяемые записи сортирует и использует их как рудиментарную объектную систему, так что программа узнает о свойствах задействованных объектов (здесь, людей) только из их использования -- вместо того, чтобы человек-программист внедрял свое понимание, априори фиксируя конкретное представление указанных объектов в программе (например, 5-кортежей и т. д.).

Конечная цель maplist( length, P, _ ) замораживает эти расширяемые записи (реализованные как открытые списки для простоты), помещая [] в их часовых (попробуйте, например, X = [1, 2 | _], length( X, _ ), чтобы увидеть, что он делает).

Цель length( P, _ ) с самого начала предусматривает подход итеративного углубления. К сожалению, без него предикат зацикливается.

Для хорошей распечатки мы можем запустить ее через

test :- puzzle( P, Pet ), 
        maplist( sort, P, PS ), maplist( writeln, PS ), nl, 
        writeln( Pet ).

производство вывода (выровнено вручную)

62 ?- test, !.
[drinks-water, house-yellow,keeps-cats,  nation-norse, smokes-dunhill   ]
[drinks-tea,   house-blue,  keeps-horses,nation-dane,  smokes-blends    ]
[drinks-milk,  house-red,   keeps-birds, nation-brit,  smokes-pallmall  ]
[drinks-beer,  house-white, keeps-dogs,  nation-swede, smokes-bluemaster]
[drinks-coffee,house-green, keeps-zebra, nation-german,smokes-prince    ]

dogs
true.
person Will Ness    schedule 26.03.2020