I have a function that allows the user to enter a single quoted string (A), then searches a list of sublists (X) for the string and returns the sublist that contains the string:
process(6, X) :-
nl, write('\tEnter student name or ID: '),
read(A),
% FIRST WE CHECK FOR A in the ID SLOT
member( [A,B,C] , X)
% IF TRUE, DISPLAY DATA THEN PASS X back to menu
-> write('\tID="'), write(A), write('", Name="'), write(B), write('", Grade='), write(C),nl,nl, menu(X)
% ELSE, WE CHECK FOR A in the NAME SLOT
; write(A), write(' not found in ID slot'), nl, checkNAME(A, X).
checkNAME(A,X) :-
member( [B,A,C] , X )
% IF TRUE (A in NAME SLOT)
-> blah blah
% ELSE
; blah blah
My issue is when i use variable A in the ELSE clause, i get junk data "L1_233" or w/e and this prevents the ELSE clause from working properly. I tried to create a copy of A and use it in the ELSE clause, but all it does is create a copy of junk data. Why does variable A work in the IF CLAUSE but not the ELSE? thanks
You're having an operator precedence issue.
In Prolog, "AND" (the comma) has a higher precedence than ->. So the nl, write(...), read(A) is all considered part of the first half (antecedent) of ->. If the antecedent fails, then Prolog will backtrack to a point before read(A) and A won't be instantiated if the "else" path is taken. You need to parenthesize:
process(6, X) :-
nl, write('\tEnter student name or ID: '),
read(A),
% FIRST WE CHECK FOR A in the ID SLOT
( member( [A,B,C] , X)
% IF TRUE, DISPLAY DATA THEN PASS X back to menu
-> write('\tID="'), write(A),
write('", Name="'), write(B),
write('", Grade='), write(C),
nl,nl, menu(X)
% ELSE, WE CHECK FOR A in the NAME SLOT
; write(A), write(' not found in ID slot'),
nl, checkNAME(A, X)
).
"AND" (comma) is also higher precedence than "OR" (semicolon) so parenthetical grouping isn't needed for the consequent and else AND sequences.
Related
Here's the code in prolog:
son(blake, john).
son(blake, katey).
son(toney, john).
son(toney, katey).
son(flory, john).
son(flory, katey).
son(charlie, stark).
son(charlie, shrek).
son(valenti, stark).
son(valenti, shrek).
age(blake, 13).
age(toney, 15).
age(flory, 19).
age(charlie, 48).
age(valenti, 49).
The definition of predicate and rules are:
son(X, Y) means X is the son of Y
age(M, N) means age of M is N
siblings(P, Q) means P and Q are siblings
My question is how to make a rule, let it be named oldestSon(X) that true if X is the oldestSon of a family?
When the query is oldestSon(flory) and oldestSon(valenti) it returns true or yes, and when the query is oldest(toney) it returns false or no.
I give it a try by writing these lines of code:
oldestSon(X) :-
son(X, _),
son(Y, _),
siblings(X, Y),
age(X, ageX),
age(Y, ageY),
ageX >= ageY.
But, when I try to input a query, in any of the case, when the query is oldestSon(blake). It returns like this:
false ? ;
false ? ;
false ? ;
...
How to make it only make only one output without using any external library or making another rule?
Your code is not working because:
it doesn't correctly describe the conditions for someone to be the oldest son in a family, and
it uses constants (ageX and ageY) where it should be using variables (AgeX and AgeY).
Assuming that "the oldest son in a family is the one who has no older sibling", you can code:
oldestSon(X) :-
age(X, AgeX),
not( ( siblings(X, Y),
age(Y, AgeY),
AgeY > AgeX ) ).
I am also assuming that the predicate siblings/2 is already defined as:
siblings(X, Y) :-
son(X, Z),
son(Y, Z),
X \= Y.
What i'm trying to do is:
Given a list of characters to know which list best contradicts it, so image I put on the list [kraken,scorpia,zulrah] so it will check the type of attack of each and would see would be the most effective type of attack for each and with that, I would receive a list of 3 bosses.
% boss(Name, Type) Name = Name of boss, Type = Attack type
boss(kraken,magic).
boss(scorpia,melee).
boss(zulrah,ranged).
boss(cerberus,melee).
boss(abyssal_sire,melee).
boss(obor,melee).
boss(sarachnis,ranged).
boss(skotizo,melee).
boss(venenatis,magic).
superEffective(magic,melee). %magic is more effective against melee
superEffective(ranged,magic). %ranged is more effective against magic
superEffective(melee,ranged). %melee is more effective against ranged
First, create a predicate to verify the counter of a single Boss:
verify_con(Boss, ConBoss) :- boss(Boss,MainSkill),
superEffective(MainSkill,ConSkill),
boss(ConBoss,ConSkill), !.
Notice that this predicate will get always the first best counter to the input boss. If you want all the possible combinations, just delete the , ! at the end.
Second, use recursion to iterate over the input list and build the output list. You can use append/3 to append the output array.
verify_con_list([],[]).
verify_con_list([H|T], LIST) :- verify_con(H, ConBoss),
verify_con_list(T, L1),
append([ConBoss],L1, LIST).
If necessary, you can define the append/3 function at the top of your code like this:
append([], X, X).
append([H|T], X, [H|S]) :- append(T, X, S).
Examples
Single output:
?- verify_con(kraken, A).
A = scorpia
List input:
?- verify_con_list([kraken, scorpia, zulrah], Con).
Con = [scorpia, zulrah, kraken]
Full code:
append( [], X, X).
append( [X | Y], Z, [X | W]) :- append( Y, Z, W).
% boss(Name, Type) Name = Name of boss, Type = Attack type
boss(kraken,magic).
boss(scorpia,melee).
boss(zulrah,ranged).
boss(cerberus,melee).
boss(abyssal_sire,melee).
boss(obor,melee).
boss(sarachnis,ranged).
boss(skotizo,melee).
boss(venenatis,magic).
superEffective(magic,melee). %magic is more effective against melee
superEffective(ranged,magic). %ranged is more effective against magic
superEffective(melee,ranged). %melee is more effective against ranged
verify_con(Boss, ConBoss) :- boss(Boss,MainSkill),
superEffective(MainSkill,ConSkill),
boss(ConBoss,ConSkill), !.
verify_con_list([],[]).
verify_con_list([H|T], LIST) :- verify_con(H, ConBoss),
verify_con_list(T, L1),
append([ConBoss],L1, LIST).
%verify_con(kraken, A).
%verify_con_list([kraken, scorpia, zulrah], Con).
My code:
run_insert_format([]).
run_insert_format([H|T]) :- H = [X,Y],
assertz(( X :- (verificar(Y)) )), run_insert_format(T).
run_query :-
odbc_query(
bd,
sqlQueryString,
List,
[ findall([Animal,Characteristic],row(Animal,Characteristic)) ]),
run_insert_format(List).
The List variable is giving me something like this:
[ [chita, mamifero], [chita, carnivoro], [chita, color_rojizo] ]
I'm trying to get this as a result of the asssertz:
%Reglas (rules)
% chita :-
% verificar(mamifero),
% verificar(carnivoro),
% verificar(color_rojizo).
But instead I get this:
% chita :-
% verificar(mamifero).
% chita :-
% verificar(carnivoro).
% chita :-
% verificar(color_rojizo).
I know what the code is doing correctly, but
What can I do to get the second result?.
Thanks in advance
To restate the problem, for each head foo, you're asserting a clause for every condition f1, f2, i.e.,
foo :- f1.
foo :- f2.
but what you want is to assert a single clause for each head with all conditions conjoined into a single body:
foo :- f1, f2.
The key step you're missing is converting the collection of characteristics for each animal into a conjunction, which you can then assert as the body.
Here is one way to achieve this result using swi-prolog association lists for grouping the characteristics:
:- use_module(library(assoc)).
assert_characteristics(Characteristics) :-
group_characteristics(Characteristics, GroupedCharacteristics),
maplist(assert_condition, GroupedCharacteristics).
% Conditions should be a conjunction, e.g., `(a, b, c)`
assert_condition(Head-Conditions) :-
maplist(make_check, Conditions, Checks),
list_to_conjunction(Checks, Conjunctions),
assertz(( Head :- Conjunctions )).
% ex. group_characteristics([foo-f1, foo-f2, bar-b1], [foo-[f1,f2], bar-[b1]]).
group_characteristics(AnimalCharacteristics, Grouped) :-
empty_assoc(Assoc),
group_characteristics(AnimalCharacteristics, Assoc, Grouped).
% helper for group_characteristics/2
group_characteristics([], Assoc, Grouped) :- assoc_to_list(Assoc, Grouped).
group_characteristics([Animal-Char|Rest], Assoc0, Grouped) :-
% Updating an existing animal with the new characteristic `Char`
( get_assoc(Animal, Assoc0, Chars, Assoc1, [Char|Chars]), !
% Otherwise, the key for `Animal` isn't present yet, so add it.
; put_assoc(Animal, Assoc0, [Char], Assoc1) ),
group_characteristics(Rest, Assoc1, Grouped).
% Convert a list of predictes into a conjunction of predicates
% ex. list_to_conjunction([a,b,c], (a, (b, (c, true)))).
list_to_conjunction([], true).
list_to_conjunction([P|Ps], (P, Conjuncts)) :- list_to_conjunction(Ps, Conjuncts).
% just a helper used in assert_condition/1
make_check(C, verificar(C)).
Example usage:
?- assert_characteristics([foo-foo1, bar-bar1, foo-foo2]).
true.
?- listing(foo).
:- dynamic foo/0.
foo :-
verificar(foo2),
verificar(foo1),
true.
There are a number of improvements and optimizations to be added to my example, such as making the grouping operation pure or collecting the characteristics into a set to prevent redundant conditions being included in the bodies.
I have recently discovered the language Prolog and have been doing exercises on its basics. I am currently creating a database on animal classes like mammals, birds and reptiles, I want to expand the database by having a size comparison within the animals but not sure how.
Here is my database.
warm_blooded(bat).
warm_blooded(penguin).
cold_blooded(crocodile).
has_fur(bat).
has_feathers(penguin).
has_scales(crocodile).
gives_birth_live(bat).
lays_eggs(penguin).
lays_eggs(crocodile).
produces_milk(bat).
has_lungs(crocodile).
has_lungs(bat).
has_lungs(penguin).
%% if the being belongs to the mammalai class ,mammalia being the scientific word for mammal
mammalia(X) :-
warm_blooded(X),
produces_milk(X),
(
has_fur(X)
;
gives_birth_live(X)
),
format('~w ~s mammal ~n', [X, "is a"]).
%% if the being belongs to the aves class aves being the scientific word for bird
aves(X) :-
warm_blooded(X),
has_feathers(X),
lays_eggs(X),
has_lungs(X),
format('~w ~s bird ~n', [X, "is a"]).
%% if the being belongs to the reptillia class(reptillia being the scientific word for reptile
reptillia(X) :-
cold_blooded(X),
lays_eggs(X),
has_scales(X),
has_lungs(X),
format('~w ~s reptile ~n', [X, "is a"]).
I've tried adding sizes within the parameters but I keep getting compilation errors. I want to have an output wherein the user is able to determine which animal is bigger when compared with each other.
A simple an effective way is to just associate a size fact with each animal.
size(bat,1).
size(penguin,2).
size(crocodile,3).
Then add one predicate with two clauses to chose the larger of the two animals.
larger(A,B,A) :-
size(A,S1),
size(B,S2),
S1 > S2.
larger(A,B,B) :-
size(A,S1),
size(B,S2),
S2 >= S1.
Examples:
?- larger(penguin,crocodile,X).
X = crocodile.
?- larger(penguin,bat,X).
X = penguin ;
false.
?- larger(bat,bat,X).
X = bat.
Note that for examples where the the second animal is smaller, it tries the first clause and succeeds, but then has a choice point and so tries the second clause and fails. This is the pure solution.
If you want to use a cut to avoid the choice point, which is impure, you can do the following
larger_2(A,B,A) :-
size(A,S1),
size(B,S2),
S1 > S2,
!.
larger_2(A,B,B) :-
size(A,S1),
size(B,S2),
S2 >= S1,
!.
Examples:
?- larger_2(penguin,crocodile,X).
X = crocodile.
?- larger_2(penguin,bat,X).
X = penguin.
?- larger_2(bat,bat,X).
X = bat.
Another way as noted by Daniel Lyons is to use ->/2
larger_3(A,B,Larger) :-
size(A,SA),
size(B,SB),
(
SA > SB
->
Larger = A
;
Larger = B
).
This variation is not one operator of just ->/2 but a combination of both ->/2 and ;2.
This also does not leave a choice point and is impure because it too uses a cut (!). Using listing/1 we can see the implementation in Prolog.
?- listing('->'/2).
:- meta_predicate 0->0.
system:A->B :-
call(( A
-> B
)).
true.
?- listing(;/2).
:- meta_predicate 0;0.
system:A:B;A:C :- !,
call(A:(B;C)).
system:A:B;C:D :-
call(A:(B;C:D)).
true.
Notice the cut !.
How the two operators work together is noted in the SWI-Prolog documentation.
The combination ;/2 and ->/2 acts as if defined as:
If -> Then; _Else :- If, !, Then.
If -> _Then; Else :- !, Else.
If -> Then :- If, !, Then.
One other point to note about the use of ->/2 with ;/2 is that the syntactic layout among many Prolog programmers is to use () with the combination and offset the operators ->/2 and ;2 so that the ; stands out.
(
% condition
->
% true
;
% false
)
When a ; is used as an OR operator and not offset the ; is often overlooked in doing a quick scan of the source code as it is seen as a comma , instead of a ;.
Also note the absence of . or , after
SA > SB
and
Larger = A
and
Larger = B
but at the end an operator is needed,
).
I'm trying to make a prolog function. The function reads in a sentence, and then tries to extract a key word. If a key word is found, it prints a message. I want it to also print a message if no keywords are found. Here is my example :
contains([word1|_]) :- write('word1 contained').
contains([Head|Tail]) :- Head \= word1, contains(Tail).
contains([word2|_]) :- write('word2 contained').
contains([Head|Tail]) :- Head \= word2, contains(Tail).
contains([word3|_]) :- write('word3 contained').
contains([Head|Tail]) :- Head \= word3, contains(Tail).
The above code will check and see if the extracted word is present. But it does not give an answer if the words 'word1,word2 or word3' are not contained. Does anybody know how I should go about implementing this?
I tried adding :
contains([_|_]) :- write('nothing contained'),nl.
contains([Head|Tail]) :- Head \= _, contains(Tail).
But clearly this is the wrong thing to do.
The standard way to write the main part of your contains predicate is:
contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail).
Which means:
when you find a word, don't search any further (this is what the cut (!) operator is for).
when nothing else worked, recurse on tail.
To add an answer in case nothing is found, just add another cut on the recursive call, so that the later case is only called when nothing else (including recursion) worked:
contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail), !.
contains(_) :- write('Nothing found').
In imperative language you'd use some kind of flag; for example:
found = False
for word in wordlist:
if word in ('car', 'train', 'plane'):
print "Found: " + word
found = True
if not found:
print "Nothing found."
You can implement this flag as another parameter to your clauses:
% entry point
contains(X) :- contains(X, false).
% for each word...
contains([Word|Rest], Flag) :-
Word = car -> (write('Car found.'), nl, contains(Rest, true)) ;
Word = train -> (write('Train found.'), nl, contains(Rest, true)) ;
Word = plane -> (write('Plane found.'), nl, contains(Rest, true)) ;
contains(Rest, Flag).
% end of recursion
contains([], true).
contains([], false) :- write('Nothing found.'), nl.
If you want to make distinct clause for each word (and abstract the loop), change the middle part to:
% for each word...
contains([Word|Rest], Flag) :-
checkword(Word) -> NewFlag=true ; NewFlag=Flag,
contains(Rest, NewFlag).
% and at the end:
checkword(car) :- write('Car found.'), nl.
checkword(plane) :- write('Plane found.'), nl.
checkword(train) :- write('Train found.'), nl.
Here is how I would do this:
contains(Words) :-
findall(Word,has(Words,Word),Sols),
print_result(Sols).
% Word is a target word in the list Words
has(Words,Word) :-
member(Word,Words),
member(Word,[word1,word2,word3]).
print_result([]) :- write('Nothing found.\n').
print_result([X|Xs]) :- print_sols([X|Xs]).
print_sols([]).
print_sols([X|Xs]) :-
concat(X, ' contained.\n',Output),
write(Output),
print_sols(Xs).
The advantage of this approach is that it uses a higher level of abstraction, making the predicate easier to read. Since there is just one list of target words, it also becomes easier to maintain, rather than having to add a separate clause for each new word.
The trick is with the has predicate which uses member/2 twice; once to select an item from the input list, and a second time to test that it is one of the target words. Using this as an argument to findall/3 then yields all the target words that were found in the input list.
Note: The [X|Xs] in print_results just avoids having to use a cut in the first clause.
I think that liori has the best answer. Here is a slightly different approach that might make sense in some cases, i.e.:
generate a print-out
if the print-out is empty then print "Nothing found", otherwise output the print-out
The following works in SWI-Prolog and probably not in other Prologs because it uses with_output_to/2:
% Define what are the keywords
keyword(word1).
keyword(word2).
keyword(word3).
% Define how the found keywords are pretty-printed
print_keyword(W) :-
format("Found: ~w.~n", [W]).
% Generate a print-out and output it unless its empty
print_keywords(Words) :-
with_output_to(atom(PrintOut),
forall((member(W, Words), keyword(W)), print_keyword(W))),
(
PrintOut == ''
->
writeln('Nothing found.')
;
write(PrintOut)
).