Prolog: How to Verify User Input? - input

I'm new to Prolog, and trying to implement an example from a textbook. I have been able to get the example working (through definite help from previous Stack Overflow posts!) finally, but now it is asking me to verify the user input: "Modify the given decision tree program, so that when the user responds to a question with an illegal answer, the system will ask him/her to reenter an answer which is among the specified choices."
In the below program, for marital status, the specified only options considered in the decision tree are "single" or "married." If you enter anything else, the goal is to get you to reenter your decision.
This is not working for me. My code is below:
:-dynamic income/2.
:-dynamic marital_status/2.
:-dynamic mortgage/2.
:-dynamic age/2.
marital_status(joe,married).
income(joe,60000).
mortgage(joe,20000).
age(joe,45).
main(X,Z):-var(X), write('what is your name?'),read(X), invest(X,Z),!.
main(X,Z):-invest(X,Z),!.
ask_marital_status(X,Y):-marital_status(X,Y).
ask_marital_status(X,Y):-not(marital_status(X,Y)), write('what is your marital status: married or single?'), read(Y), nl, asserta(marital_status(X,Y)).
ask_marital_status(X,Y):-Y \=married, Y \=single, write('what is your marital status: married or single?'), read(Y), nl, asserta(marital_status(X,Y)).
%ask_marital_status(X,Y):-Y \= married; Y \= single, ask_marital_status(X,Y).
ask_income(X,Y):-income(X,Y).
ask_income(X,Y):-not(income(X,Y)),write('what is your annual income?'), nl, read(Y), asserta(income(X,Y)).
ask_mortgage(X,Z):-mortgage(X,Z).
ask_mortgage(X,Z):-not(mortgage(X,Z)), write('what is your mortgage?'), read(Z), nl, asserta(mortgage(X,Z)).
ask_age(X,A):-age(X,A).
ask_age(X,A):-not(age(X,A)), write('what is your age?'), read(A), nl, asserta(age(X,A)).
moderate_risk(X):-ask_marital_status(X,Y), Y=married, ask_income(X,I), I=<50000, ask_mortgage(X,Z), Z=<50000,!.
moderate_risk(X):-ask_marital_status(X,M), M=married, ask_income(X,I), I=<50000,!.
moderate_risk(X):-ask_marital_status(X,M), M=single, ask_income(X,I), I=<35000,!.
stable_risk(X):-ask_marital_status(X,M), M=married, ask_income(X,I), I=<50000, ask_mortgage(X,Z), Z>50000,!.
stable_risk(X):-ask_marital_status(X,M), M=single, ask_income(X,I), I>35000, ask_age(X,A), A>50, !.
high_risk(X):-ask_marital_status(X,M), M=single, ask_income(X,I), I>35000, ask_age(X,A), A=<50, !.
invest(X,oil):-stable_risk(X),!.
invest(X,telecommunications):-moderate_risk(X),!.
invest(X,computers):-high_risk(X),!.
The third "ask_martial_status" is my current attempt to get the user to reenter their decision, but it is not working. I have tried it with Prolog's AND operator(,) and their or operator (;) - neither make a difference (for what I place in between Y\= married and Y\=single). When I enter erroneous input, I just get "false" returned. Below is an example:
?- main(X,Z).
what is your name?logan.
what is your marital status: married or single?|: widowed.
false.
The commented out line (with the %) was a previous attempt to get the program working that also failed. I'm surprised I wasn't able to find a quick YouTube video / article to read when I Google'd this issue. Could anyone help me out here?

The traditional way of reading and validating user input is to use the following template:
ask(Data) :-
repeat,
write(Prompt),
read(Data),
valid(Data),
!.
Applying this template to your case, we can write:
ask_marital_status(Status) :-
repeat,
write('What is your marital status (married or single)?'),
read(Status)
( Status == married
; Status == single
),
!.
As a general rule, is best practice to separate asking the user input from processing the input (asserting a fact in your case).
High-level solutions would use message printing and question asking mechanisms for user interaction (see e.g. https://logtalk.org/2019/11/14/abstracting-user-interaction.html). But that's a more advanced topic.

You can solve your problem in this way (snippet of the involved prefocate):
ask_marital_status(X,Y):-
\+ marital_status(X,Y),
write('what is your marital status: married or single?'),
read(Y1),
( (Y1 == single ; Y1 == married) ->
Y = Y1,
asserta(marital_status(X,Y));
ask_marital_status(X,Y)
).
You can check if the value you read (Y1) is single or married with (Y1 = single ; Y1 = married) (; means or). Then, if so (->) you proceed with the rest of the predicate. Otherwise, (after the ; next to asserta/1) you call recursively marital_status/2.
EDIT: thanks to Paulo Moura's comment, =/2 (unification) must be replaced with ==/2 (equality), in order to make the code behave as expected.

Related

How to keep translations separated where the same word is used in English but a different one in other languages?

Imagine I have a report, a letter actually, which I need to translate to several languages. I have created a greeting field in the form which is filled programatically by an onchange event method.
if self.partner_id.gender == 'female':
self.letter_greeting = _('Dear %s %s,') % ( # the translation should be "Estimada"
self.repr_recipient_id.title.shorcut, surname
)
elif self.partner_id.gender == 'male':
self.letter_greeting = _('Dear %s %s,') % ( # translation "Estimado"
self.repr_recipient_id.title.shorcut, surname
)
else:
self.letter_greeting = _('Dear %s %s,') % ( # translation: "Estimado/a"
self.partner_id.title.shorcut, surname
)
In that case the word Dear should be translated to different Spanish translations depending on which option is used, this is because we use different termination depending on the gender. Exporting the po file I found that all the options are altogether, that make sense because almost all the cases the translations will be the same, but not in this case:
#. module: custom_module
#: code:addons/custom_module/models/sale_order.py:334
#: code:addons/custom_module/models/sale_order.py:338
#: code:addons/custom_module/models/sale_order.py:342
#, python-format
msgid "Dear %s %s,"
msgstr "Dear %s %s,"
Solutions I can apply directly
Put all the terms in different entries to avoid the same translation manually every time I need to update the po file. This can be cumbersome if you have many different words with that problem. If I do it and I open the file with poedit, this error appears: duplicate message definition
Put all the possible combinations with slashes, this is done y some other parts of Odoo. For both gender would be:
#. module: stock
#: model:res.company,msg:stock.res_company
msgid "Dear"
msgstr "Estimado/a"
This is just an example. I can think of many words that look the same in English, but they use different spelling or meanings in other languages depending on the context.
Possible best solutions
I don't know if Odoo know anything aboutu the context of a word to know if it was already translated or not. Adding a context manually could solve the problem, at least for words with different meanings.
The nicest solution would be to have a parameter to the translation module to make sure that the word is exported as an isolated entry for that especific translation.
Do you think that I am giving to it too much importance haha? Do you know if there is any better solution? Why is poedit not taking into account that problem at all?
I propose an extension of models res.partner.title and res.partner.
res.partner.title should get a translateable field for saving salutation prefixes like 'Dear' or 'Sehr geehrter' (German). Maybe it's worth to get something about genders, too, but i won't get into detail here about that.
You probably want to show the configuring user an example like "Dear Mr. Name" or something like that. A computed field should work.
On res.partner you should just implement either a computed field or just a method to get a full salutation for a partner record.
To some degree this is a linguistics problem. I believe the best solution would be to use a different "Source Language", one made up of keys, and then have English as another Translation. The word "Dear" in English does not have a gender context (and typically, much of English doesn't), while the word "Estimado" in Spanish does. The translation from that Spanish word to English is more appropriately "Masculine Dear." Therefore, using keys as your source language, you would have this:
SourceText (EnglishDescription) -> Translation (English) -> Translation (Spanish)
DearMasculine -> Dear -> Estimado
DearFeminine -> Dear -> Estimada
DearNuetral -> Dear -> Estimado/a

Sentence segmentation and dependency parser

I’m pretty new to python (using python 3) and spacy (and programming too). Please bear with me.
I have three questions where two are more or less the same I just can’t get it to work.
I took the “syntax specific search with spacy” (example) and tried to make different things work.
My program currently reads txt and the normal extraction
if w.lower_ != 'music':
return False
works.
My first question is: How can I get spacy to extract two words?
For example: “classical music”
With the previous mentioned snippet I can make it extract either classical or music. But if I only search for one of the words I also get results I don’t want like.
Classical – period / era
Or when I look for only music
Music – baroque, modern
The second question is: How can I get the dependencies to work?
The example dependency with:
elif w.dep_ != 'nsubj': # Is it the subject of a verb?
return False
works fine. But everything else I tried does not really work.
For example, I want to extract sentences with the word “birthday” and the dependency ‘DATE’. (so the dependency is an entity)
I got
if d.ent_type_ != ‘DATE’:
return False
To work.
So now it would look like:
def extract_information(w,d):
if w.lower_ != ‘birthday’:
return False
elif d.ent_type_ != ‘DATE’:
return False
else:
return True
Does something like this even work?
If it works the third question would be how I can filter sentences for example with a DATE. So If the sentence contains a certain word and a DATE exclude it.
Last thing maybe, I read somewhere that the dependencies are based on the “Stanford typed dependencies manual”. Is there a list which of those dependencies work with spacy?
Thank you for your patience and help :)
Before I get into offering some simple suggestions to your questions, have you tried using displaCy's visualiser on some of your sentences?
Using an example sentence 'John's birthday was yesterday', you'll find that within the parsed sentence, birthday and yesterday are not necessarily direct dependencies of one another. So searching based on the birthday word having a dependency of a DATE type entity, might not be yield the best of results.
Onto the first question:
A brute force method would be to look for matching subsequent words after you have parsed the sentence.
doc = nlp(u'Mary enjoys classical music.')
for (i,token) in enumerate(doc):
if (token.lower_ == 'classical') and (i != len(doc)-1):
if doc[i+1].lower_ == 'music':
print 'Target Acquired!'
If you're unsure of what enumerate does, look it up. It's the pythonic way of using python.
To questions 2 and 3, one simple (but not elegant) way of solving this is to just identify in a parsed sentence if the word 'birthday' exists and if it contains an entity of type 'DATE'.
doc = nlp(u'John\'s birthday was yesterday.')
for token in doc:
if token.lower_ == 'birthday':
for entities in doc.ents:
if entities.label_ == 'DATE':
print 'Found ya!'
As for the list of dependencies, I presume you're referring to the Part-Of-Speech tags. Check out the documentation on this page.
Good luck! Hope that helped.

Am I training my wit.ai bot correctly?

I'm trying to train my Wit.ai bot in order to recognize the first name of someone. I'm not very sure if I well understand how the NLP works so I'll give you an example.
I defined a lot of expressions like "My name is XXXX", "Everybody calls me XXXX"
In the "Understanding" table I added an entity named "contact_name" and I add almost 50 keywords like "Michel, John, Mary...".
I put the trait as "free-text" and "keywords".
I'm not sure if this process is correctly. So, I ask you:
does it matter the context like "My name is..." for the NLP? I mean...will it help the bot to predict that after this expression probably a fist name will come on?
is that right to add like 50 values to an entity or it's completly wrong?
what do you suggest as a training process in order to get the first name of someone?
You have done it right by keeping the entity's search strategy as "free-text" and "Keywords". But Adding keywords examples to the entity doesn't make any sense because a person's name is not a keyword.
So, I would recommend a training strategy which is as follows:
Create various templates of the message like, "My name is XYZ", "I am XYZ", "This is XYZ" etc. (all possible introduction messages you could think of)
Remove all keywords and expressions for the entity you created and add these two keywords:
"a b c d e f g h i j k l m n o p q r s t u v w x y z"
"XYZ" (can give any name but maintain this name same for validating the templates)
In the 'Understanding' tab enter the messages and extract the name into the entity ("contact_name" in your case) and validate them
Similarly, validate all message templates keeping the name as "XYZ"
After you have done this for all templates your bot will be able to recognise any name in a given template of the message
The logic behind this is your entity is a free-text and keyword which means it first tries to match the keyword if not matched it tries to find the word in the same position of the templates. Keeping the name same for validations helps to train the bot with the templates and learn the position where the name will be usually found.
Hope this works. I have tried this and worked for me. I am not sure how bot trains in background. I recommend you to start a new app and do this exercise.
Comment if there is any problem.
wit.ai has a pre-trained entity extraction method called wit/contact, which
Captures free text that's either the name or a clear reference to a
person, like "Paul", "Paul Smith", "my husband", "the dentist".
It works good even without any training data.
To read about the method refer to duckling.

Password-checking with prolog

I want to write a prolog program which tells me if a password fulfills the rules, which are:
the password must contain a letter(a-z),
a number(0-9),
a double letter (aa,ll,ww etc.),
must start with a letter (a, aa, c etc.),
must be at least 6 characters long
How can I do that? And how should I "ask questions" after that? (Q1:Is this password correct? Q2:Is it long enough?)
Thank you for your answer in advance. It's very important and I really appreciate it.
Well, I'd go with something like that, but there are maybe far better solutions using DCG or something else :
contains_letter(Password) :- wildcard_match('*[a-zA-Z]*', Password).
contains_number(Password) :- wildcard_match('*[0-9]*', Password).
contains_double_letter(Password) :-
(between(65, 90, Letter) ; between(97, 122, Letter)),
append([_, [Letter, Letter], _], Password),
!.
starts_with_letter(Password) :- wildcard_match('[a-zA-Z]*', Password).
long_enough(Password) :-
length(Password, Length),
Length >= 6.
check_everything(Password) :-
contains_letter(Password),
contains_number(Password),
contains_double_letter(Password),
starts_with_letter(Password),
long_enough(Password).
Usage would be :
?- check_everything("password_to_test").
or just one of the criterias :
?- long_enough("password_to_test").
Note that it uses a swi-pl predicate wildcard_match/2 and that I do not know if it is available in other implementations.
I hope this can be of some help !
Note that contains_letter/1, contains_number/1 and starts_with_letter/1 can all be combined into one wildcard_match/2 btw :
starts_with_letter_has_number(Password) :-
wildcard_match('[a-zA-Z]*[0-9]*', Password).

Referencing associations with Ruleby on Rails

I've recently started messing with a gem called 'ruleby', a rule-engine for Ruby. The documentation for ruleby is a bit sparse, however, and I can't seem to figure out how to properly reference associations for the rule-writing bit. I'm stumped both the 'pattern' part of the rule and also in the executing block part of the rule.
For example, let's say I had a rule which would only be executed only when a user submitted a positive review. I could, for instance, write the following:
rule :positive_review, [Review, :review, method.review_rating == "positive"] do |v|
assert (store positive_review somehow)
end
So it's at this point that I get lost. I would like to write a rule which would reference back to the user and check the total number of positive reviews that the user of this positive review and possibly execute certain actions based on this number.
If anyone could point me in the right direction, I would be greatly appreciative. Thanks.
I dont quite understand why you are asserting a fact inside of a rule, that should already be done. The github example code probably answered you by now but in case not:
First you initialise the engine, I did it like this so I could call on it more than once.
rule_engine = Ruleby::Core::Engine.new
RULES_ENG = RulesEngineCaller.new(rule_engine)
EngineRulebook.new(rule_engine).rules
Then you assert your facts
#events = Event.all
#events.each do |event|
#rule_engine.assert event
end
Then you run your engine
#rule_engine.match
if you want to add events you can just by calling assert per fact to the existing engine, it will just add to it until you either delete inside the engine or rebuild it.
The rulebook is where you add your facts for me I did it programatically:
class EngineRulebook < Rulebook
def rules
Rails.logger.debug "#{Time.now.utc} - Loading Rules into Rulebook"
#msg = Notification.new()
#rules = Rule.all
#rules.each do |rule_name|
case
#hard coded rule - for auto cleaning
when (rule_name.title == "SYSTEM ADMIN - auto-clean delete rule") && (rule_name.group.title == "Notification Admin")
rule [Event, :m, m.terminate_flag == 1] do |v|
if v[:m].rules.include?(rule_name) == false
#output matched rule to console & logfile
Rails.logger.info "#{Time.now.utc} - match rule #{rule_name.id} #{rule_name.title} - #{v[:m].ticket_id} - #{v[:m].description}"
#add reference so rule doesn't fire again
#event = Event.find_by_id(v[:m].id)
#event.rules << rule_name
v[:m].rules << rule_name
modify v[:m]
#Retract v[:m] would remove the fact from the rules engine, need to remove all related facts though
#so dont use this as other rules may be requires as rules do not fire in order
end
end
end
I also did it as a case statement as I was loading rules programmatically. I also put a check in to say has this rule run before because each time you run the engine it will assess all facts, even ones that have already matched.
Hopefully you have an answer, if not hopefully this helped.