如何在Prolog中返回建议列表?

时间:2019-03-08 17:54:44

标签: prolog prolog-findall

对于我的任务,我应该列出20只潜在的宠物,然后定义有关每只宠物的事实。然后,我需要问潜在的宠物主人五个问题,这些问题将有助于确定哪些宠物是好的建议。我正在尝试根据用户输入返回宠物列表,但每次都返回true,而实际上并没有列出推荐的宠物。不知道我要去哪里错了。我只会在代码示例中包含一些宠物,因此它不会太长。

pet_advisor.pl:

pet(cat).
pet(chameleon).
pet(chicken).
pet(chinchilla).
pet(cow).

size(cat, small).
sleeps(cat, night).
stays(cat, indoor).
stays(cat, outdoor).
class(cat, mammal).
live(cat, 12)

size(chameleon, small).
sleeps(chameleon, night).
stays(chameleon, indoor).
class(chameleon, reptile).
live(chameleon,5).

size(chicken, small).
sleeps(chicken, night).
stays(chicken, outdoor).
class(chicken, bird).
live(chicken,10).

size(chinchilla, small).
sleeps(chinchilla, day).
stays(chinchilla, indoor).
class(chinchilla, mammal).
live(chinchilla,15).

size(cow, large).
sleeps(cow, night).
stays(cow, outdoor).
class(cow, mammal).
live(cow,22).

pet_size_ok(X) :- pet_size(X), size(Y, X).
sleep_type_ok(X) :- sleep_type(X), sleeps(Y, X).
pet_location_ok(X) :- pet_location(X), stays(Y, X).
kind_ok(X) :- kind(X), class(Y, X).
life_ok(X) :- life(X), live(Y, Z), Z =< X.

which_pet(X) :- pet_size_ok(X), sleep_type_ok(X), pet_location_ok(X), kind_ok(X), life_ok(X).

recommend :- write('Do you want a small, medium, or large sized pet? '), read(Size), nl, assert(pet_size(Size)),
             write('Do you want a pet that sleeps during the day or night? '), read(Sleep), nl, assert(sleep_type(Sleep)),
             write('Do you want an indoor or outdoor pet? '), read(Place), nl, assert(pet_location(Place)),
             write('Do you want a reptile, mammal, bird, or a fish? '), read(Type), nl, assert(kind(Type)),
             write('How long do you want your pet to live (years)? '), read(Age), nl, assert(life(Age)),
             findall(Pets, which_pet(Pets), Suggestions),
             write('I would recommend these pets for you: '), nl, writelist(Suggestions),
             retract(pet_size(Size)), retract(sleep_type(Sleep)), 
             retract(pet_location(Place)),
             retract(kind(Type)), retract(life(Age)).

writelist([]).
writelist([H|T]) :- writeonce(H,T), writelist(T).  
writeonce(H,T) :- member(H,T).
writeonce(H,T) :- not(member(H,T)), write(H), nl.

因此,如果我要回答以下问题: 小 晚 室内 哺乳动物 15

它应该返回带有[cat,chinchilla]的列表,但返回的所有内容都是正确的。

1 个答案:

答案 0 :(得分:3)

您的代码有几个问题。首先,对于大多数Prolog系统和Prolog标准,必须声明不连续谓词。在文件开头添加以下指令:

:- discontiguous([
    size/2, sleeps/2, stays/2, class/2, live/2
]).

接下来,不需要使用动态谓词以及在查询建议时断言和撤回事实:

which_pet(Size, Sleep, Place, Type, Age, Pet) :-
    size(Pet, Size),
    sleeps(Pet, Sleep),
    stays(Pet, Place),
    class(Pet, Type),
    live(Pet, Age0), Age0 =< Age.

recommend :-
    write('Do you want a small, medium, or large sized pet? '), read(Size), nl,
    write('Do you want a pet that sleeps during the day or night? '), read(Sleep),
    write('Do you want an indoor or outdoor pet? '), read(Place), nl,
    write('Do you want a reptile, mammal, bird, or a fish? '), read(Type), nl,
    write('How long do you want your pet to live (years)? '), read(Age), nl,
    findall(Pet, which_pet(Size,Sleep,Place,Type,Age,Pet), Suggestions),
    write('I would recommend these pets for you: '), nl, writelist(Suggestions).

这不是理想的重写方法,因为它的伸缩性较差,但比使用动态谓词要好得多。

最后,用于打印结果的代码执行了两个任务,这些任务可以更好地分开:(1)过滤重复项,(2)打印唯一结果。我建议您将这些任务分开。过滤结果可以例如完成通过使用setof/2而不是findall/3或在sort/2调用构造的列表中调用findall/3来实现。我把那封信留给你。还要使用标准的否定控制构造\+/1,而不是旧的/弃用的not/1谓词。

通话示例:

| ?- recommend.
Do you want a small, medium, or large sized pet? small.
Do you want a pet that sleeps during the day or night? night.
Do you want an indoor or outdoor pet? outdoor.
Do you want a reptile, mammal, bird, or a fish? bird.

How long do you want your pet to live (years)? 20.

I would recommend these pets for you: 
chicken

yes