How Do I Compare a List to a Database Prolog - list

I have a database of facts that has entries like this snippet
symptom(shingles,headache).
symptom(shingles,fever).
symptom(shingles,malaise).
symptom(shingles,headache).
symptom(shingles,itching).
symptom(shingles,hyperesthesia).
symptom(shingles,paresthesia).
test(shingles,blood).
test(shingles,pcr).
locale(shingles,all).
treatment(shingles,calamine).
treatment(shingles,aciclovir).
treatment(shingles,valaciclovir).
treatment(shingles,famciclovir).
treatment(shingles,corticosteroids).
I then have a predicate that gets a list of symptoms from the user.
getSymptoms(Symptoms) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Symptoms = []
;
getSymptoms(Symptoms0),
Symptoms = [Response|Symptoms0]
).
My question is how do I compare the lists of user symptoms to the symptoms fact second atom, and then add the disease to another list?
For example, the user enters fever. Since fever is in the symptom fact for shingles, it would add shingles to a list.

This will work, but allows for duplicate symptoms to be entered. I am posting it now so you can see the first part of the transformation and will post the part without the duplicates when I get that working.
getSymptoms(Symptoms) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Symptoms = []
;
atom_string(Symptom,Response),
valid_symptom(Symptom,Symptoms)
).
valid_symptom(Symptom,Symptoms) :-
(
symptom(_,Symptom)
->
% Symptom was valid
% so get next symptom and
% add to list on backtracking
getSymptoms(Symptoms0),
Symptoms = [Symptom|Symptoms0]
;
% Symptom was invalid
% so warn user of invalid symptom and what they input
% and get next symptom.
% Do not add invalid Symptom to list on backtracking.
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms(Symptoms0),
Symptoms = Symptoms0
).
Since the values entered are strings and the symptoms are atoms in symptom/2 facts, the input needs to be converted to an atom for comparison. This is done using atom_string/2
atom_string(Symptom,Response)
To give a user feedback if the symptom is invalid format/2 is used. It is better to use than write/1 since it gives you more control over the output.
format('Invalid symptom: `~w''~n',[Symptom])
If an invalid value is entered for as a symptom it should not be added to the list. This is a classic if/then type of scenario and in Prolog is done with ->/2. Here is the standard template
(
<conditional>
->
<true branch>
;
<false branch>
)
the conditional is
symptom(_,Symptom)
Notice also with the conditional, that it reads the symptom/2 facts and ignores the first part of the compound structure, i.e. _, and matches the input symptom with a symptom in the facts. This is were the comparison is done, but it is done via unification and not with a compare predicate such as ==/2.
The true branch is the same as before
getSymptoms(Symptoms0),
Symptoms = [Symptom|Symptoms0]
however the false branch is
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms(Symptoms0),
Symptoms = Symptoms0
Notice that the invalid Symptom is not added to the list with [Symptom|Symptoms0] and also that both branches (true and false) should update the same variables, Symptoms, which in the false branch is done with Symptoms = Symptoms0 which is not assignment but =/2 (unification).
The code for valid_symptom/2 could have been inlined with getSymptoms/1 but I pulled it out so you could see how it is done in case you need to do that in the future.
Example run:
?- getSymptoms(Symptoms).
Please enter symptoms now, enter "Done" when finished: wrong
Invalid symptom: `wrong'
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: malaise
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: Done
Symptoms = [headache, malaise, headache].
Here is the next variation that removes duplicates while building the list.
getSymptoms(Result) :-
getSymptoms_helper([],Result).
getSymptoms_helper(Symptoms,Result) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Result = Symptoms
;
atom_string(Symptom,Response),
valid_symptom(Symptom,Symptoms,Result)
).
valid_symptom(Symptom,Symptoms,Result) :-
(
memberchk(Symptom,Symptoms)
->
% Symptom was a duplicate
% Do not add duplicate Symptom to list.
getSymptoms_helper(Symptoms,Result)
;
(
symptom(_,Symptom)
->
% Symptom was valid
% so get next symptom and
% add to list.
getSymptoms_helper([Symptom|Symptoms],Result)
;
% Symptom was invalid
% so warn user of invalid symptom and what they input
% and get next symptom.
% Do not add invalid Symptom to list.
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms_helper(Symptoms,Result)
)
).
The major change here is that an accumulator Symptoms is threaded through the predicates so that the list of valid symptoms can be built and used for testing the next input value. Since the accumulator needs to be initialized at the start, the previous predicate renamed to getSymptoms_helper so that the accumulator can be initialized with
getSymptoms_helper([],Result)
Notice [] that passes to
getSymptoms_helper(Symptoms,Result)
thus setting the initial value of Symptoms to [].
When Done is entered, the list is unified with Result and passed back upon back-chaining. Normally the variables would be named Symptoms0 and Symptoms but I kept them this way so they are easier to follow. Other wise there would be variables Symptom, Symptoms and Symptoms0, but once you get use to them are easier to follow.
The check for duplicates is done using memberchk/2 which is better than using member/2 for checking. Again this adds another conditional to the mix.
Example run:
?- getSymptoms(Symptoms).
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: malaise
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: Done
Symptoms = [malaise, headache].

Related

Power Query: try ... otherwise on list item expression

Why does catching the following index-error fail:
let
Source = "abc",
letter_list = Text.ToList(Source),
try_on_index_error = try letter_list{5}
in
try_on_index_error
The try statement does not return an Error record. It keeps throwing the error as if no try was present.
In this case it works as expected, returning an Error record as a valid result of the query:
let
Source = "abc",
try_another_error = try Source + 1,
Error = try_another_error[Error]
in
Error
https://learn.microsoft.com/en-us/power-query/handlingerrors#catching-an-error-with-try-and-otherwise
https://learn.microsoft.com/en-us/powerquery-m/m-spec-consolidated-grammar#error-handling-expression
It is naturally hard to answer questions on the behavior of closed-source software. However, the comment by Ron Rosenfeld solved my trouble:
letter_list = List.Buffer(Text.ToList(Source))
The docs (last paragraph in chapter Evaluation) explain the unexpected behavior: Lazy evaluation is done for lists, records and let-expressions. As seen above, in case of lazy evaluation, try statement falls back to syntax checking, supposedly.

Python 3.7 : Why is only the last item in a list printing?

I am self-studying "Python Crash Course" 2nd edition and I have come upon a problem that I am unable to solve. I have reread previous areas of the book, searched the internet and search other SO answers.
The official book answer uses a dictionary but I am trying to use a list.
The program should ask for input, add the input to a list and continue to repeat until told to stop. When told to stop, the entire list should be printed.
The problem that I am running into is that only the last item in the list in printing.
Instead of giving the answer, please just give me a hint so that I can truly learn.
Thanks,
chadrick
active = True
while active == True:
places = []
place = input("\nIf you could visit one place in the world, where would you go? ")
places.append(place)
repeat = input("Would you like to let another person respond? (yes/no) ")
if repeat == 'no':
print(places)
active = False
The reason is because you're resetting the list back to empty for every iteration.
try the following:
active = True
places = []
while active == True:
place = input("\nIf you could visit one place in the world, where would you go? ")
places.append(place)
repeat = input("Would you like to let another person respond? (yes/no) ")
if repeat == 'no':
print(places)
active = False
Also, since active is a boolean, you just need to do while active:
So:
active = True
places = []
while active:
place = input("\nIf you could visit one place in the world, where would you go? ")
places.append(place)
repeat = input("Would you like to let another person respond? (yes/no) ")
if repeat == 'no':
print(places)
active = False

Returning to the beginning of a piece of code when if statement condition is not met

I am totally new to programming and I have been trying to get a simple piece of code working. However I keep getting multiple different errors so I assume I am doing something very wrong somewhere along the line... Here is my code at the moment:
userName = input('Please enter your name: ')
age = input('Please enter your age: ')
if int(age) <= 5:
print(userName, 'you are too young to play') break
else:
print (userName, 'Your old enough')
factor = 2
finalAge = int(age) + int(factor)
multAge = int(age) * int(factor)
divAge = float(age) / int(factor)
print('In', factor, 'years you will be', finalAge, 'years old', userName )
print('Your age multiplied by', factor, 'is', multAge)
print('Your age divided by', factor, 'is', divAge)
What I want to do is, if the user's age is not higher than 5, then they get the message that they are too young to play and it returns to the start of the piece of code - asking for the name again.
Does anyone have any advice on how to do this?
You'll need to use a loop. Syntax depends on language, which you haven't specified. As pseudo-code, you could do
loop indefinitely
prompt for name and age
if age is less than 5
print error
otherwise
print that age is ok
break loop
Take a look at while loops. For this you'd be able to set some kind of condition (such as an "old_enough" variable and when that becomes true, the loop stops running.
You would set this value inside the if statement. By default it should be whatever will make the loop run
There's loads of tutorials online for this, but here's an example in python (your code sample looks like Python3):
old_enough = False
while not old_enough:
# do something
if age > 5:
print("You're old enough")
old_enough = True
else:
print("you're not old enough")
That should make sense. if not, please do look up documentation. It'll be better for you in the long term

How to create multiple loops in python

I'm trying to create a basic maths program which will randomly generate a numerical question and then allows 3 attempts to complete it before it moves onto the next question however cant figure out how to make it do both together.
My code currently looks like this
print "What is your name?"
score = 0
Attempt = 0
loop = True
Name = raw_input()
import random
for i in range (1,6):
question_1_part_1 = random.randint(1,30)
question_1_part_2 = random.randint(1,30)
print "What is", question_1_part_1, "+", question_1_part_2, "?"
while Attempt <3: # inputing this while loop here means it will retry the same question.
Guess = raw_input()
Guess = int(Guess)
answer = question_1_part_1 + question_1_part_2
if Guess == answer:
print "Well done"
score = score + 1
else: print "try again"
Attempt = Attempt + 1
if Attempt == 3: print "You failed"
print Name, "your score is", score
A simple break statement will take you out of the loop when the answer is correct.
if Guess == answer:
print "Well done"
score += 1
break
else: print "try again"
Note the change to your assignment; this is considered a cleaner increment.
To answer your comment ... you don't use this when the person gets the question wrong. Your program logic freezes out the user after three wrong guesses total, on all questions. If you want them to have up to three guesses on every problem, then you have to reset the Attempt counter for every question. Pull that line into the outer loop:
for i in range (1,6):
Attempt = 0
loop = True
question_1_part_1 = random.randint(1,30)
question_1_part_2 = random.randint(1,30)
In the future, I strongly recommend that you program incrementally: get a few lines working before you add more. Insert tracing print statements to check that the values and program flow are correct. Don't remove them until that part of the code is solid -- and even then only comment them out until the entire program is working. Some of your problems stem from trying to write more than you can comfortably hold in your mind at once -- which is common at most levels of programming. :-)
Add another argument to your while loop.
while Attempt <3 and Guess != ... :
rest of loop code
Then you are exiting your loop when they get the correct answer. Or you could break from the statement when they get the answer right.

Creating a list from user input with swi-prolog

This is my first experience with Prolog. I am at the beginning stages of writing a program that will take input from the user (symptoms) and use that information to diagnose a disease. My initial thought was to create lists with the disease name at the head of the list and the symptoms in the tail. Then prompt the user for their symptoms and create a list with the user input. Then compare the list to see if the tails match. If the tails match then the head of the list I created would be the diagnosis. To start I scaled the program down to just three diseases which only have a few symptoms. Before I start comparing I need to build the tail of list with values read from the user but I can't seem to get the syntax correct.
This is what I have so far:
disease([flu,fever,chills,nausea]).
disease([cold,cough,runny-nose,sore-throat]).
disease([hungover,head-ache,nausea,fatigue]).
getSymptoms :-
write('enter symptoms'),nl,
read(Symptom),
New_Symptom = [Symptom],
append ([],[New_symptom],[[]|New_symptom]),
write('are their more symptoms? y or n '),
read('Answer'),
Answer =:= y
-> getSymptoms
; write([[]|New_symptom]).
The error occurs on the append line. Syntax Error: Operator Expected.
Any help with this error or the design of the program in general would be greatly appreciated.
This is one way to read a list of symptoms in:
getSymptoms([Symptom|List]):-
writeln('Enter Symptom:'),
read(Symptom),
dif(Symptom,stop),
getSymptoms(List).
getSymptoms([]).
You type stop. when you want to finish the list.
You would then need to decide what logic you want to match the way you have represented a disease.
A complete example:
:-dynamic symptom/1.
diagnose(Disease):-
retractall(symptom(_)),
getSymptoms(List),
forall(member(X,List),assertz(symptom(X))),
disease(Disease).
getSymptoms([Symptom|List]):-
writeln('Enter Symptom:'),
read(Symptom),
dif(Symptom,stop),
getSymptoms(List).
getSymptoms([]).
disease(flue):-
symptom(fever),
symptom(chills),
symptom(nausea).
disease(cold):-
symptom(cough),
symptom(runny_nose),
symptom(sore_throat).
disease(hungover):-
symptom(head_ache),
symptom(nausea),
symptom(fatigue).
create(L1):-read(Elem),create(Elem,L1).
create(-1,[]):-!.
create(Elem,[Elem|T]):-read(Nextel),create(Nextel,T).
go:- write('Creating a list'),nl,
write('Enter -1 to stop'),nl,
create(L),
write('List is:'),
write(L).