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)
).
Related
I want to add in the DB a constant and a linked variable:
?- assertz(my(x, A))
So that in the future I can define A and get the only one entry. Sth like that:
?- assertz(my(x, A)), ..., A = 2.
?- my(A, B).
A = x,
B = 2.
Can this be done?
As I noted in the comments your idea of a link like a pointer is not the way to approach solving your problem.
A common solution is to walk the tree and construct a new tree as you walk the tree by replacing the leaf of the tree with a new leaf that contains the value from the input tree along with the associated value, what you are thinking should be linked.
Since you are somewhat new to Prolog I will do this with two examples. The first will just walk a tree and only return true when successfully walked. It can be used to understand how to walk a tree and run with gtrace to single step the code to understand it.
The second example will expand on the tree walk and add the type (link as you think) to the leaf item. The the old leaf for something simple like an atom a, will become a new leaf in the tree like (a,atom).
Also this was quickly written as a demonstration only. I am sure it will have problems if pressed into doing anything more than the single example.
:- module(example,
[
example/1
]).
example(walk) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
walk(Term).
example(infer_type) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
infer_type(Term,Is),
write(Is).
walk([]) :- !.
walk([T]) :- var(T), !.
walk(L) :- is_list(L), !, L = [H|T], walk(H), walk(T).
walk(T) :- compound(T), !, T =.. [_|Args], !, walk(Args).
walk(T) :- integer(T), !.
walk(T) :- var(T), !.
walk(T) :- atomic(T), !.
walk(T) :- T =.. [Arg|Args], !, walk(Arg), walk(Args).
infer_type([],[]) :- !.
infer_type([T],[(T,var)]) :- var(T), !.
infer_type(L,S) :- is_list(L), !, L = [H|T], infer_type(H,I), infer_type(T,Is), S = [I|Is].
infer_type(T,S) :- compound(T), !, T =.. [F|Args], !, infer_type(Args,Is), S =.. [F|Is].
infer_type(T,(T,integer)) :- integer(T), !.
infer_type(T,(T,var)) :- var(T), !.
infer_type(T,(T,atom)) :- atomic(T), !.
infer_type(T,S) :- T =.. [Arg|Args], !, infer_type(Arg,I), infer_type(Args,Is), S =.. [I|Is].
Example run
Note: I know there are warnings; it is a demo not production code.
Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.3)
?- working_directory(_,'C:/Users/Groot').
true.
?- [example].
Warning: c:/users/Groot/example.pl:20:
Warning: Singleton variables: [T]
Warning: c:/users/Groot/example.pl:24:
Warning: Singleton variables: [T]
true.
?- example(walk).
true.
?- example(infer_type).
term_size(a((1,integer),(Hello,atom),(Atom,atom),(1,integer)+(2,integer),[(a,atom),(($),atom),(_25642,var)]))
true.
As an exercise I did not identify the string as a string, the change should be easy.
All of these predicates are defined in pretty much the same way. The base case is defined for the empty list. For non-empty lists we unify in the head of the clause when a certain predicate holds, but do not unify if that predicate does not hold. These predicates look too similar for me to think it is a coincidence. Is there a name for this, or a defined abstraction?
intersect([],_,[]).
intersect(_,[],[]).
intersect([X|Xs],Ys,[X|Acc]) :-
member(X,Ys),
intersect(Xs,Ys,Acc).
intersect([X|Xs],Ys,Acc) :-
\+ member(X,Ys),
intersect(Xs,Ys,Acc).
without_duplicates([],[]).
without_duplicates([X|Xs],[X|Acc]) :-
\+ member(X,Acc),
without_duplicates(Xs,Acc).
without_duplicates([X|Xs],Acc) :-
member(X,Acc),
without_duplicates(Xs,Acc).
difference([],_,[]).
difference([X|Xs],Ys,[X|Acc]) :-
\+ member(X,Ys),
difference(Xs,Ys,Acc).
difference([X|Xs],Ys,Acc) :-
member(X,Ys),
difference(Xs,Ys,Acc).
delete(_,[],[]).
delete(E,[X|Xs],[X|Ans]) :-
E \= X,
delete(E,Xs,Ans).
delete(E,[X|Xs],Ans) :-
E = X,
delete(E,Xs,Ans).
There is an abstraction for "keep elements in list for which condition holds".
The names are inclide, exclude. There is a library for those in SWI-Prolog that you can use or copy. Your predicates intersect/3, difference/3, and delete/3 would look like this:
:- use_module(library(apply)).
intersect(L1, L2, L) :-
include(member_in(L1), L2, L).
difference(L1, L2, L) :-
exclude(member_in(L2), L1, L).
member_in(List, Member) :-
memberchk(Member, List).
delete(E, L1, L) :-
exclude(=(E), L1, L).
But please take a look at the implementation of include/3 and exclude/3, here:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/apply.pl?show=src#include/3
Also in SWI-Prolog, in another library, there are versions of those predicates called intersection/3, subtract/3, delete/3:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#intersection/3
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#subtract/3
https://www.swi-prolog.org/pldoc/doc_for?object=delete/3
Those are similar in spirit to your solutions.
Your next predicate, without_duplicates, cannot be re-written like that with include/3 or exclude/3. Your implementation doesn't work, either. Try even something easy, like:
?- without_duplicates([a,b], L).
What happens?
But yeah, it is not the same as the others. To implement it correctly, depending on whether you need the original order or not.
If you don't need to keep the initial order, you can simply sort; this removes duplicates. Like this:
?- sort(List_with_duplicates, No_duplicates).
If you want to keep the original order, you need to pass the accumulated list to the recursive call.
without_duplicates([], []).
without_duplicates([H|T], [H|Result]) :-
without_duplicates_1(T, [H], Result).
without_duplicates_1([], _, []).
without_duplicates_1([H|T], Seen0, Result) :-
( memberchk(H, Seen0)
-> Seen = Seen0 , Result = Result0
; Seen = [H|Seen0], Result = [H|Result0]
),
without_duplicates_1(T, Seen, Result0).
You could get rid of one argument if you use a DCG:
without_duplicates([], []).
without_duplicates([H|T], [H|No_duplicates]) :-
phrase(no_dups(T, [H]), No_duplicates).
no_dups([], _) --> [].
no_dups([H|T], Seen) -->
{ memberchk(H, Seen) },
!,
no_dups(T, Seen).
no_dups([H|T], Seen) -->
[H],
no_dups(T, [H|Seen]).
Well, these are the "while loops" of Prolog on the one hand, and the inductive definitions of mathematical logic on the other hand (See also: Logic Programming, Functional Programming, and Inductive Definitions, Lawrence C. Paulson, Andrew W. Smith, 2001), so it's not surprising to find them multiple times in a program - syntactically similar, with slight deviations.
In this case, you just have a binary decision - whether something is the case or not - and you "branch" (or rather, decide to not fail the body and press on with the selected clause) on that. The "guard" (the test which supplements the head unification), in this case member(X,Ys) or \+ member(X,Ys) is a binary decision (it also is exhaustive, i.e. covers the whole space of possible X)
intersect([X|Xs],Ys,[X|Acc]) :- % if the head could unify with the goal
member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
intersect([X|Xs],Ys,Acc) :- % if the head could unify with the goal
\+ member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
Other applications may need the equivalent of a multiple-decision switch statement here, and so N>2 clauses may have to be written instead of 2.
foo(X) :-
member(X,Set1),
(...action...).
foo(X) :-
member(X,Set2),
(...action...).
foo(X) :-
member(X,Set3),
(...action...).
% inefficient pseudocode for the case where Set1, Set2, Set3
% do not cover the whole range of X. Such a predicate may or
% may not be necessary; the default behaviour would be "failure"
% of foo/1 if this clause does not exist:
foo(X) :-
\+ (member(X,Set1);member(X,Set2);member(X,Set3)),
(...action...).
Note:
Use memberchk/2 (which fails or succeeds-once) instead of member/2 (which fails or succeeds-and-then-tries-to-succeed-again-for-the-rest-of-the-set) to make the program deterministic in its decision whether member(X,L).
Similarly, "cut" after the clause guard to tell Prolog that if a guard of one clause succeeds, there is no point in trying the other clauses because they will all turn out false: member(X,Ys),!,...
Finally, use term comparison == and \== instead of unification = or unification failure \= for delete/3.
I have written a predicate that prints out each element in the list except the last. The last element should be handled differently; it should print LAST! instead. This is what I have.
write_data([]).
write_data([X]) :-
!, write('LAST!'), nl.
write_data([X | Rest]) :-
write(x), nl,
write_data(Rest).
Is there a better way? Is there a way to do this without the cut?
You can avoid the cut, by performing unification with a list that has at least two elements, like:
write_data([]).
write_data([_]) :-
write('LAST!'),
nl.
write_data([X|Rest]) :-
Rest = [_|_],
write(X), nl,
write_data(Rest).
We can furthermore avoid the double unpacking with a helper predicate:
write_data([]).
write_data([H|T]) :-
write_data(T, H).
write_data([], _) :-
write('LAST!'), nl.
write_data([H|T], X) :-
write(X), nl,
write_data(T, H).
A common definition for a last/2 predicate that provides access to the last element of a list is:
last([Head| Tail], Last) :-
last(Tail, Head, Last).
last([], Last, Last).
last([Head| Tail], _, Last) :-
last(Tail, Head, Last).
When called with the first argument bound to a closed list, the auxiliary predicate, last/3, avoids spurious choice-points assuming a Prolog system implementing, as common, first-argument indexing. Can you modify this predicate to do what you want?
The general rule of thumb for cut removal is to note what was true in the clause that contains the cut , then make sure that is false in the other clause(s) .
Thus :
write_data([]).
write_data([X]) :-
/*!,*/write('LAST!'), nl.
write_data([X | Rest]) :-
dif(Rest,[]) , /**/
write(x), nl,
write_data(Rest).
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 am doing Prolog Programming for my research and I got some problems..
First, all of my codes are below.
%% Lines are without period(.)
diagnosis :-
readln(Line1),
readln(Line2),
readln(Line3),
readln(Line4),
readln(Line5),
readln(Line6),
readln(Line7),
readln(Line8),
readln(Line9),
readln(Line10),
write(Line1),nl,
write(Line2),nl,
write(Line3),nl,
write(Line4),nl,
write(Line5),nl,
write(Line6),nl,
write(Line7),nl,
write(Line8),nl,
write(Line9),nl,
write(Line10),nl.
%% (get_symptom(Line1,[man]) -> write('man!!!!!!!!!')),
%% (get_symptom(Line2,[woman]) -> write('woman!!!!!!!!!')).
%% if A then B else C, (A->B; C)
%% grammar
s --> np, vp.
np --> det, n.
vp --> v, np.
det --> [a].
n --> [man].
v --> [has].
n --> [woman].
n --> [fever].
n --> [runny_nose].
get_symptom(Line,N) :- s(Line,[]), member(N,Line).
member(X, [X|T]).
member(X,[H|T]) :-
member(X,T).
%% FindSymptom(Line, [Symptom]) : - s(Line,[]), np(_, _, object,[a,
%% Symptom]), n(singular, [Symptom], []).
start :-
write('What is the patient''s name? '),
readln(Patient), %% Here, this can be used for input of all symtoms
diagnosis,
hypothesis(Patient,cold,S1),
append([cold/S1/red],[],N1), write(S1),
write('until...'),
hypothesis(Patient,severe_cold,S2), write(S2),
append([severe_cold/S2/red],N1,BarList),
write('until...'),
%% write(Patient,"probably has ",Disease,"."),nl.
hypothesis(Patient,Disease,100),
write(Patient),
write(' probably has '),
write(Disease),
write('.'),
test_barchart(BarList).
start :-
write('Sorry, I don''t seem to be able to'),nl,
write('diagnose the disease.'),nl.
symptom(Patient,fever) :-
get_symptom(Line1, [fever]);
get_symptom(Line2, [fever]);
get_symptom(Line3, [fever]);
get_symptom(Line4, [fever]);
get_symptom(Line5, [fever]);
get_symptom(Line6, [fever]);
get_symptom(Line7, [fever]);
get_symptom(Line8, [fever]);
get_symptom(Line9, [fever]);
get_symptom(Line10, [fever]).
symptom(Patient,runny_nose) :-
get_symptom(Line1, [runny_nose]);
get_symptom(Line2, [runny_nose]);
get_symptom(Line3, [runny_nose]);
get_symptom(Line4, [runny_nose]);
get_symptom(Line5, [runny_nose]);
get_symptom(Line6, [runny_nose]);
get_symptom(Line7, [runny_nose]);
get_symptom(Line8, [runny_nose]);
get_symptom(Line9, [runny_nose]);
get_symptom(Line10, [runny_nose]).
hypothesis(Patient,cold,Score_Cold) :-
(symptom(Patient,fever), Score_Cold is 100),write('!!!');
Score_Cold is 0.
hypothesis(Patient,severe_cold,Score_Severe) :-
((symptom(Patient,fever), Score1 is 50);
Score1 is 0),
((symptom(Patient, runny_nose), Score2 is 50);
Score2 is 0),
Score_Severe is Score1 + Score2.
%% hypothesis(Disease) :-
%%(hypothesis1(Patient,cold,Score1),
%%Score1 =:= 100 -> Disease = cold);
%%(hypothesis2(Patient,severe_cold,Score2),
%%Score2 =:= 100 -> Disease = severe_cold).
%% make graph for the result
:- use_module(library(pce)).
:- use_module(library(plot/barchart)).
:- use_module(library(autowin)).
test_barchart(BarList):-
new(W, picture),
send(W, display, new(BC, bar_chart(vertical,0,100))),
forall(member(Name/Height/Color,
BarList),
( new(B, bar(Name, Height)),
send(B, colour(Color)),
send(BC, append, B)
)),
send(W, open).
%% [X/100/red, y/150/green, z/80/blue, v/50/yellow]
%% append List
append([], L, L).
append([H|T], L2, [H|L3]):-
append(T, L2, L3).
As you can see, I want to make a bar_graph from 10 input lines by extracting symptoms..
But when I executed this code, I got the result as below...
1 ?- start.
What is the patient's name? GJ
|: a man has a runny_nose
|: a
|: a
|: a
|: a
|: a
|: a
|: a
|: a
|: a
[a,man,has,a,runny_nose]
[a]
[a]
[a]
[a]
[a]
[a]
[a]
[a]
[a]
!!!100until...100until...!!![GJ] probably has cold.
true
I only typed one symptom (runny_nose). I want to get Score for "cold" is 0, Score for "severe_cold" is 50 and BarGraph result... But What Happened? I can't find..
*****Edited******
I found that the problem is related to local variables (Line1, .. ,Line10) But how can I deal with? If I can make all the variables; Line1, ... , Line10 as global variables then, I think the problem can be solved...
****Addition*****
I changed my 'start' predicate as follows...I didn't use 'diagnosis' and 'hypothesis' predicates/ But the problems is maybe.. 'get_symptoms' predicate. Is there any choice I can choose except that I don't use 'get_symptoms' and 'symptoms' predicates...? Then the code will become very coarse...
start :-
write('What is the patient''s name? '),
readln(Patient), %% Here, this can be used for input of all symtom
readln(Line1),
readln(Line2),
readln(Line3),
readln(Line4),
readln(Line5),
readln(Line6),
readln(Line7),
readln(Line8),
readln(Line9),
readln(Line10),
(symptom(Patient,fever,Line1,Line2,Line3,Line4,Line5,Line6,Line7,Line8,Line9,Line10) -> (Cold is 80, Severe_Cold is 50)),
(symptom(Patient,runny_nose,Line1,Line2,Line3,Line4,Line5,Line6,Line7,Line8,Line9,Line10) -> Severe_Cold is Severe_Cold + 50),
write(Severe_Cold), write(Cold),
append([cold/Cold/red],[],N1),
append([severe_cold/Severe_Cold/red],N1,BarList),
%% write(Patient,"probably has ",Disease,"."),nl.
write(Severe_Cold),
((Cold =:= 100 -> Disease = cold) ; (Severe_Cold =:= 100 -> Disease = severe_cold)),
write(Patient),
write(' probably has '),
write(Disease),
write('.'),
test_barchart(BarList).
When programming in Prolog, you need to do some research into the language to understand how it works. Many Prolog beginners make the mistake of learning a couple of snippets of Prolog logic and then apply what they know of other languages to attempt to create a valid Prolog programming. However, Prolog doesn't work like other common languages at all.
Regarding variables, there are no globals. Variables are always "local" to a predicate clause. A predicate clause is one of one or more clauses that describe a predicate. For example:
foo(X, Y) :- (some logic including X and Y).
foo(X, Y) :- (some other logic including X and Y).
foo(X, X) :- (some other logic including X).
These are three clauses describing the predicate foo/2. The value of X or Y instantiated in one clause isn't visible to the other clauses.
If you want to instantiate a variable in one predicate and use it in another, you have to pass it as a parameter:
foo([1,2,3,4], L),
bar(L, X).
Here, foo might instantiate L using some logic and based upon the instantiated value of [1,2,3,4] for the first argument. Then L (now instantiated) is passed as the first argument to the predicate bar.
If you need a value to be persistent as data, you could assert it as a fact as follows:
foo :-
(some logic that determines X),
assertz(my_fact(X)),
(more logic).
bar :-
my_fact(X), % Will instantiate X with what was asserted
(some logic using X).
This would work, but is not a desirable approach in Prolog to "fake" global variables. Asserting items into persistent data is designed for the purpose of maintaining a Prolog database of information.
So you can see that the logic you have involving Line1, Line2, ... will not work. One clue would be that you must have received many warnings about these variables being "singleton". You need to study Prolog a bit more, then recast your logic using that knowledge.