2

有三个朋友 - misha、petya、vova。姓氏:伊万诺夫、谢苗诺夫、格拉西莫夫。

name(misha).
name(vova).
name(petya).
surname(ivanov).
surname(semyonov).
surname(gerasimov).

米莎不是格拉西莫夫。

full_name(misha,X) :- 
surname(X),
X \= gerasimov.  

vova 正在学习 6 年级。格拉西莫夫排在第五位。如何定义 vova 的姓氏(几个)?

vova 的父亲是工程师,ivanov 的父亲是锁匠。

father(vova,ingeneer).
father(ivanov,locksmith).

vova的姓(一)怎么定义?

4

2 回答 2

2

首先,我们需要捕捉到每个名字都有一个给定的姓氏这一事实。

is_one_of(Name, [ivanov, semyonov, gerasimov]) :-
    member(Name, [misha, vova, petya]).

我们还被赋予了关于姓名和姓氏的特征(学习、父亲),每一个都有不同的价值。这由以下谓词捕获。

name_characteristic(vova, studying, sixth_grade).
name_characteristic(vova, father, ingeneer).

surname_characteristic(gerasimov, studying, fifth_grade).
surname_characteristic(ivanov, father, locksmith).

我们也被赋予gerasimov is_not了姓氏misha

is_not(misha, gerasimov).

规则一:Name不能有Surname如果对于某些共同的特征,它们的值是不同的。

is_not(Name, Surname) :-
    name_characteristic(Name, Characteristic, Value1),
    surname_characteristic(Surname, Characteristic, Value2),
    Value1 \= Value2.

规则 2:如果其他可能的姓氏都不是 的姓氏,则Name必须有。SurnameName

is_(Name, Surname) :-
    is_one_of(Name, Surnames),
    select(Surname, Surnames, RemainingSurnames),
    is_none_of(Name, RemainingSurnames).

is_none_of(_, []).
is_none_of(Name, [Surname | RemainingSurnames]) :-
    is_not(Name, Surname),
    is_none_of(Name, RemainingSurnames).

查询is(vova, Surname)收益率:

Surname = semyonov

查询is_not(Name, Surname)收益率:

Name = misha,
Surname = gerasimov

Name = vova,
Surname = gerasimov

Name = vova,
Surname = ivanov
于 2019-08-10T14:03:12.860 回答
1

这个解决方案使用chr库,它实际上是一种与 Prolog 不同的语言。我现在自己在学习 CHR,这实际上是我用它编写的第二个程序。它可能与我在邮件列表中获得的改进版本过于相似。所以带着一粒盐吃。

在我看来,CHR 似乎非常适合从很多可能性开始然后消除不良可能性的程序。也许取消可以继续,直到你收敛到一个解决方案。这就是这个程序的工作方式,我们生成所有可能的姓名/姓氏集合,然后使用我们的不可能约束来减少可能的姓氏集合,直到它们收敛到一个,然后我们将它们从其他姓名/姓氏集合中删除,直到一切收敛。

前两行介绍了我们将使用的库和约束:

:- use_module(library(chr)).
:- chr_constraint name/1, surnames/1, impossible/2, matched/2, possible/2.

第一个约束产生了我们需要走过的可能性。

gen_possibilities @
  name(Name), surnames(Surnames)
  ==> possible(Name, Surnames).

这里的想法是我们已经将姓氏放在一个列表中,我们只是possible在每个名字和可能的姓氏总数之间创建一对。

impossibility @
  impossible(Name, Surname), possible(Name, Surnames)
  <=> select(Surname, Surnames, Remaining) 
  | possible(Name, Remaining).

一旦我们被告知某事是不可能的,我们可以简单地将其从该名字的可能姓氏列表中删除。select/3对于从列表中删除单个项目并为您提供剩余部分很有用。如果姓氏已经不在可能的姓氏列表中,我们在此处使用警卫,这可能在应用第四个约束之后发生。

only_possiblity @
  possible(Name, [Surname])
  <=> matched(Name, Surname).

一旦可能性列表减少到一个名字,我们就成功地进行了匹配。

remove_matched @
  matched(_, Surname), possible(Name, _)
  ==> impossible(Name, Surname).

如果我们进行了匹配,那么对于所有其他名称,该姓氏将变得不可能。

如果由于名称已被处理而无法吸收不可能的约束,则可以添加额外的清理约束,但不是必需的:

irrelevant_impossibility @
  possible(Name, Surnames) 
  \ impossible(Name, Surname)
  <=> \+ memberchk(Surname, Surnames) 
  | true.

现在我们可以运行程序了。我冒昧地为 5 年级/6 年级和不同的父亲内联逻辑。我觉得这个意图很容易通过阅读来弄清楚,我认为通过专门建模它不会有太多收获。感谢@RobertBaron 在他的解决方案中遇到了麻烦!

main :-
    name(misha),
    name(vova),
    name(petya),
    surnames([ivanov, semyonov, gerasimov]),
    impossible(misha, gerasimov),  % stated impossible
    impossible(vova, gerasimov),   % different grades
    impossible(vova, ivanov).      % different fathers

执行此操作的结果是:

?- main.
name(petya),
name(vova),
name(misha),
surnames([ivanov, semyonov, gerasimov]),
matched(petya, gerasimov),
matched(misha, ivanov),
matched(vova, semyonov).

我相信这满足了摆在您面前的限制条件。

于 2019-08-12T20:11:51.683 回答