Prolog wildcard for completing a string - prolog

I am currently stuck on a prolog problem.
So far I have:
film(Title) :- movie(Title,_,_). (Where 'movie(T,_,_,)' is a reference to my database)
namesearch(Title, Firstword) :- film(Title), contains_term(Firstword, Title).
It is hard to explain what I need help on, but basically is there a wildcard I can use to search for all films starting with a specific word, for example, if I were to search for all films beginning with the word "The".
Is there a wildcard which would allow me to input as such: namesearch(X,'The*') ?
I have tried using the asterisk like this and it does not work,
Thank your for your help

It all depends how the title is represented.
Atom
If it is represented as an atom, you need sub_atom(Atom, Before, Length, After, Sub_atom)
?- Title = 'The Third Man', sub_atom(Title, 0, _, _, 'The').
Title = 'The Third Man'.
List of codes
If it is a list of codes which is called a string in Prologs in Edinburgh tradition, you can either "hard code" it with append/3 or you might use Definite Clause Grammars for general patterns.
?- set_prolog_flag(double_quotes,codes).
true.
?- append("The",_, Pattern), Title = "The Third Man", Pattern = Title.
Pattern = Title, Title = [84,104,101,32,84,104,105,114,100|...].
?- Title = "The Third Man", phrase(("The",...), Title).
Title = [84,104,101,32,84,104,105,114,100|...]
; false.
Note that 84 is the character code of T etc.
phrase/2 is "the entry" to grammars. See dcg for more. Above used the following definition:
... --> [] | [_], ... .
List of characters
Similar to list of codes, list of characters provide a more readable representation that has still the advantages of being compatible with list predicates and Definite Clause Grammars:
?- set_prolog_flag(double_quotes,chars).
true.
?- append("The",_, Pattern), Title = "The Third Man", Pattern = Title.
Pattern = Title, Title = ['T',h,e,' ','T',h,i,r,d|...].
?- Title = "The Third Man", phrase(("The",...), Title).
Title = ['T',h,e,' ','T',h,i,r,d|...]
; false.
See also this answer.

Related

How to get the first letter of each word enter by user

Here are the details of my code
name:-read(X),write('Name : '),write(X),nameCode(X).
nameCode(X):-nl, write('Name Code : ').
I would like to take the first 3 letters from each word and display it . What should be added to my code?
Furthermore, the result i get only allows me to enter a single name from user. When i attempt to enter several names(peter jane mary) in the query, it display a syntax message as below
| ?- name.
|: peter jane mary.
* Syntax Error
Below are the results of what i want to print
Name : peter jane mary
Name Code : PJM
1- First the user enters his/her first/middle/last name.
2- It is read.
3- string_chars breaks the string into characters : peter will become p,e,t,e,r
4- getFirstLetter Predicate extracts the first element from the list: from peter we get p.
5- upcase_atom convert lowercase letters to uppercase: p will become P.
6- display the answer using write.
k:-
write('Enter First name: '),nl,
read(FName),nl,
string_chars(FName,N1),
getFirstLetter(N1,L1),
upcase_atom(L1,Str1),
write('Enter Middle name: '),nl,
read(MName),nl,
string_chars(MName,N2),
getFirstLetter(N2,L2),
upcase_atom(L2,Str2),
write('Enter Last name: '),nl,
read(LName),nl,
string_chars(LName,N3),
getFirstLetter(N3,L3),
upcase_atom(L3,Str3),
write(Str1),write(' '),write(Str2),write(' '),write(Str3).
getFirstLetter([H|_],H).
Example:
?-k.
Enter First name:
peter
Enter Middle name:
jane
Enter Last name:
mary
P J M
___
A more challenging task: Remove the first letter from the name.
1- First the user enters his/her first/middle/last name.
2- It is read.
3- string_chars breaks the string into characters : peter will become p,e,t,e,r
4- removeFirstLetter predicate removes the first letter: p,e,t,e,r will become e,t,e,r
5- charstring predicate will convert e,t,e,r to "e","t","e","r", we do this using term_string (this is important for the next
step)
6- atomic_list_concat joins all the separate charaters together: "e","t","e","r" will
become eter
k:-
write('Enter first name: '),nl,
read(FName),nl,
string_chars(FName,N1),
removeFirstLetter(N1,L1),
charstring(L1,String1),
atomic_list_concat( String1 , Cs1),
write('Enter Middle name: '),nl,
read(MidName),nl,
string_chars(MidName,N2),
removeFirstLetter(N2,L2),
charstring(L2,String2),
atomic_list_concat( String2 , Cs2),
write('Enter Last name: '),nl,
read(LName),nl,
string_chars(LName,N3),
removeFirstLetter(N3,L3),
charstring(L3,String3),
atomic_list_concat( String3 , Cs3),
write(Cs1),write(" "),write(Cs2), write(" "),write(Cs3).
charstring([],[]).
charstring([H|T],[H2|L]):-
term_string(H,H2),
charstring(T,L).
removeFirstLetter([_|T],T).
Example:
?-k.
Enter first name:
peter
Enter Middle name:
jane
Enter Last name:
mary
eter ane ary
1true
The built-in predicate read/1 reads in a prolog term, terminated by a full-stop.
"peter jane mary" is not a prolog term, so you get a syntax error. In particular, prolog doesn't know what to do with the blank space between your names.
If you prefer to use comma to separate names, you can get this:
?- read(X), X = ','(A, ','(B,C)).
|: peter,mary,paul.
X = (peter,mary,paul),
A = peter,
B = mary,
C = paul.
The comma is interpreted as a functor (in standard prolog).
Have I replied your question ?

How can i set a newline in a set string in SWI-Prolog?

I have this string:
B='Dogs cats birds and fish'.
and i need it to appear in the format bellow:
Dogs,
cats,
birds,
and fish
Is there any possible way for this to happen?
I tried nlas in:
B='Dogs,nl, cats,nl, birds,nl, and fish'.
and \n as in:
B='Dogs,\n, cats,\n, birds,\n, and fish'.
to no avail
B='Dogs cats birds and fish'.
you should split the string as a list, and then iterate the list and write.
In Prolog, nl is a Predicate, is can not be used like \n,
print_s([]).
print_s([H|L]):-
write(H),
nl,
print_s(L).
print_s(['Dogs,', 'cats,', 'birds', 'and fish']).
The new line character works, you should use write/1 [swi-doc] to print the string to the console, like:
?- write('Dogs,\ncats,\nbirds,\nand fish').
Dogs,
cats,
birds,
and fish
true.
?- B = 'Dogs,\ncats,\nbirds,\nand fish', write(B).
Dogs,
cats,
birds,
and fish
B = 'Dogs,\ncats,\nbirds,\nand fish'.
If you unify a variable, the Prolog will print that variable as a string literal, not the content of the string.

Using a Prolog DCG to split a string

I'm trying to use a DCG to split a string into two parts separated by spaces. E.g. 'abc def' should give me back "abc" & "def". The program & DCG are below.
main:-
prompt(_, ''),
repeat,
read_line_to_codes(current_input, Codes),
(
Codes = end_of_file
->
true
;
processData(Codes),
fail
).
processData(Codes):-
(
phrase(data(Part1, Part2), Codes)
->
format('~s, ~s\n', [ Part1, Part2 ])
;
format('Didn''t recognize data.\n')
).
data([ P1 | Part1 ], [ P2 | Part2 ]) --> [ P1 | Part1 ], spaces(_), [ P2 | Part2 ].
spaces([ S | S1 ]) --> [ S ], { code_type(S, space) }, (spaces(S1); "").
This works correctly. But I found that having to type [ P1 | Part1 ] & [ P2 | Part2 ] was really verbose. So, I tried replacing all instances of [ P1 | Part1 ] w/ Part1 & likewise w/ [ P2 | Part2 ] in the definition of data, i.e. the following.
data(Part1, Part2) --> Part1, spaces(_), Part2.
That's much easier to type, but that gave me an Arguments are not sufficiently instantiated error. So it looks like an unbound variable isn't automatically interpreted as a list of codes in a DCG. Is there any other way to make this less verbose? My intent is to use DCG's where I would use regular expressions in other programming languages.
Your intuition is correct; the term-expansion procedure for DCGs (at least in SWI-Prolog, but should apply to others) with your modified version of data gives the following:
?- listing(data).
data(A, D, B, F) :-
phrase(A, B, C),
spaces(_, C, E),
phrase(D, E, F).
As you can see, the variable Part1 and Part2 parts of your DCG rule have been interpreted into calls to phrase/3 again, and not lists; you need to explicitly specify that they are lists for them to be treated as such.
I can suggest an alternative version which is more general. Consider the following bunch of DCG rules:
data([A|As]) -->
spaces(_),
chars([X|Xs]),
{atom_codes(A, [X|Xs])},
spaces(_),
data(As).
data([]) --> [].
chars([X|Xs]) --> char(X), !, chars(Xs).
chars([]) --> [].
spaces([X|Xs]) --> space(X), !, spaces(Xs).
spaces([]) --> [].
space(X) --> [X], {code_type(X, space)}.
char(X) --> [X], {\+ code_type(X, space)}.
Take a look at the first clause at the top; the data rule now attempts to match 0-to-many spaces (as many as possible, because of the cut), then one-to-many non-space characters to construct an atom (A) from the codes, then 0-to-many spaces again, then recurses to find more atoms in the string (As). What you end up with is a list of atoms which appeared in the input string without any spaces. You can incorporate this version into your code with the following:
processData(Codes) :-
% convert the list of codes to a list of code lists of words
(phrase(data(AtomList), Codes) ->
% concatenate the atoms into a single one delimited by commas
concat_atom(AtomList, ', ', Atoms),
write_ln(Atoms)
;
format('Didn''t recognize data.\n')
).
This version breaks a string apart with any number of spaces between words, even if they appear at the start and end of the string.

Regular expression to match my pattern of words, wild chars

can you help me with this:
I want a regular expression for my Ruby program to match a word with the below pattern
Pattern has
List of letters ( For example. ABCC => 1 A, 1 B, 2 C )
N Wild Card Charaters ( N can be 0 or 1 or 2)
A fixed word (for example “XY”).
Rules:
Regarding the List of letters, it should match words with
a. 0 or 1 A
b. 0 or 1 B
c. 0 or 1 or 2 C
Based on the value of N, there can be 0 or 1 or 2 wild chars
Fixed word is always in the order it is given.
The combination of all these can be in any order and should match words like below
ABWXY ( if wild char = 1)
BAXY
CXYCB
But not words with 2 A’s or 2 B’s
I am using the pattern like ^[ABCC]*.XY$
But it looks for words with more than 1 A, or 1 B or 2 C's and also looks for words which end with XY, I want all words which have XY in any place and letters and wild chars in any postion.
If it HAS to be a regex, the following could be used:
if subject =~
/^ # start of string
(?!(?:[^A]*A){2}) # assert that there are less than two As
(?!(?:[^B]*B){2}) # and less than two Bs
(?!(?:[^C]*C){3}) # and less than three Cs
(?!(?:[ABCXY]*[^ABCXY]){3}) # and less than three non-ABCXY characters
(?=.*XY) # and that XY is contained in the string.
/x
# Successful match
else
# Match attempt failed
end
This assumes that none of the characters A, B, C, X, or Y are allowed as wildcards.
I consider myself to be fairly good with regular expressions and I can't think of a way to do what you're asking. Regular expressions look for patterns and what you seem to want is quite a few different patterns. It might be more appropriate to in your case to write a function which splits the string into characters and count what you have so you can satisfy your criteria.
Just to give an example of your problem, a regex like /[abc]/ will match every single occurrence of a, b and c regardless of how many times those letters appear in the string. You can try /c{1,2}/ and it will match "c", "cc", and "ccc". It matches the last case because you have a pattern of 1 c and 2 c's in "ccc".
One thing I have found invaluable when developing and debugging regular expressions is rubular.com. Try some examples and I think you'll see what you're up against.
I don't know if this is really any help but it might help you choose a direction.
You need to break out your pattern properly. In regexp terms, [ABCC] means "any one of A, B or C" where the duplicate C is ignored. It's a set operator, not a grouping operator like () is.
What you seem to be describing is creating a regexp based on parameters. You can do this by passing a string to Regexp.new and using the result.
An example is roughly:
def match_for_options(options)
pattern = '^'
pattern << 'A' * options[:a] if (options[:a])
pattern << 'B' * options[:b] if (options[:b])
pattern << 'C' * options[:c] if (options[:c])
Regexp.new(pattern)
end
You'd use it something like this:
if (match_for_options(:a => 1, :c => 2).match('ACC'))
# ...
end
Since you want to allow these "elements" to appear in any order, you might be better off writing a bit of Ruby code that goes through the string from beginning to end and counts the number of As, Bs, and Cs, finds whether it contains your desired substring. If the number of As, Bs, and Cs, is in your desired limits, and it contains the desired substring, and its length (i.e. the number of characters) is equal to the length of the desired substring, plus # of As, plus # of Bs, plus # of Cs, plus at most N characters more than that, then the string is good, otherwise it is bad. Actually, to be careful, you should first search for your desired substring and then remove it from the original string, then count # of As, Bs, and Cs, because otherwise you may unintentionally count the As, Bs, and Cs that appear in your desired string, if there are any there.
You can do what you want with a regular expression, but it would be a long ugly regular expression. Why? Because you would need a separate "case" in the regular expression for each of the possible orders of the elements. For example, the regular expression "^ABC..XY$" will match any string beginning with "ABC" and ending with "XY" and having two wild card characters in the middle. But only in that order. If you want a regular expression for all possible orders, you'd need to list all of those orders in the regular expression, e.g. it would begin something like "^(ABC..XY|ACB..XY|BAC..XY|BCA..XY|" and go on from there, with about 5! = 120 different orders for that list of 5 elements, then you'd need more for the cases where there was no A, then more for cases where there was no B, etc. I think a regular expression is the wrong tool for the job here.

Prolog: Reading input with spaces causes an error

this is my program
tran('father','otosan').
tran('father','chichiwe').
tran('mother','okasan').
tran('mother','hahawe').
tran('hi','ohayo').
tran('good night','oyasemi').
tran('good bye','sayonara').
tran('do your best','gambaru').
tran('please','onegai').
tran('sorry','gomen').
tran('thank you','aregatto').
tran('cute','kawaii').
eng:- nl,write('enter a word in english: '),read(X),jap(X).
jap(X):- tran(X,Y),write('the word in japanese is '),write(Y),nl,fail.
jap(Z).
:-eng.
I got an error in the words with spaces
how can I solve this problem?
It appears that when inputting words with spaces, you need to surround them with single quotes:
?- ['trans.pl'].
% trans.pl compiled 0.00 sec, 5,432 bytes
true.
?- eng.
enter a word in english: hi.
the word in japanese is ohayo
false.
?- eng.
enter a word in english: 'good bye'.
the word in japanese is sayonara
false.
The reason this is happening is that when you enter hi, Prolog is unifying it to jap(hi). which resolves to ohayo. When you enter good bye, Prolog unifies it to jap(good bye), which will give you an error (Operator Expected). This is why you need to quote your input as 'good bye'. Prolog will then unify it to jap('good bye'), which gives you what you want.

Resources