Prolog remove character spaces from atom for using term_to_atom - prolog

At some point of my program I have an atom formed by what previously were also atoms, and I want to remove the character spaces within it so that later I can use without any problem:
term_to_atom(Result, 'second2(second2),region(ºMediterranean Sea),months(no_value),third3(third3),recog(ºNew Type),distance(no_value)').
and obtain this
Result = (second2(second2), region(ºMediterraneanSea), months(no_value), third3(third3), recog(ºNewType), distance(no_value))
or also the original would work
Result = (second2(second2), region(ºMediterranean Sea), months(no_value), third3(third3), recog(ºNew Type), distance(no_value))
because if I don't delete those character spaces then term_to_atom will complain about it. How can I solve it?

You can use this procedure:
strip_spaces(S, NoSpaces) :-
atom_codes(S, Cs), delete(Cs, 0' , X), atom_codes(NoSpaces, X).
but delete/3 is deprecated. Another possibility is
strip_spaces(S, NoSpaces) :-
atomic_list_concat(L, ' ', S),
atomic_list_concat(L, NoSpaces).
Either of these will 'eat' each space, but from your problem description, in comments you exchanged with gusbro, this doesn't seems to me the right way to go. Changing the literals seems at DB interface could ask for trouble later.
Parsing your input to a list, instead of a conjunction, can be done with DCGs:
:- [library(http/dcg_basics)].
parse_result(X, R) :-
atom_codes(X, Cs),
phrase(parse_list(R), Cs).
parse_list([T|Ts]) -->
parse_term(T), (",", parse_list(Ts) ; {Ts=[]}).
parse_term(T) -->
string(F), "(", string(Arg), ")",
{atom_codes(Fa,F), atom_codes(Arga,Arg), T =.. [Fa,Arga]}.

Related

Prolog member function should work but it doesnt

I am trying to check if a string is a working call that can be executed. To do this I parse the string, get the first word, and if it matches a database of predefined functions, it should succeed.
Q has the string, A will be used later, not now. Example of string is: append a and b.
is_uni(Q, A):-
split_string(Q, " ", ",", [X|Y]),
uni_db(Z),
member(X, Z).
uni_db([
append,
member,
append1
]).
You need to use atom_codes/2 predicate to convert stings to atoms, in example you need to convert "append" to append in order to work.
is_uni(Q,A):-
split_string(Q, " ", ",", [X|Y]),
atom_codes(W,X),
uni_db(Z),
member(W, Z).
Example:
?- is_uni("append a and b",A).
true ;
false.
You are confusing strings with atoms.
"append" and 'append', a.k.a. append, are different. You can use atom_string/2 to convert between them:
..., atom_string(A, X), ...
You are re-implementing built-in features.
Why store commands in a list and iterate with member/2? Just define some facts:
uni_db(append).
uni_db(member).
uni_db(append1).
And then, you simply have to check whether uni_db(A). This is better supported by an implementation and more likely to be done efficiently.

Prolog build rules from atoms

I'm currently trying to to interpret user-entered strings via Prolog. I'm using code I've found on the internet, which converts a string into a list of atoms.
"Men are stupid." => [men,are,stupid,'.'] % Example
From this I would like to create a rule, which then can be used in the Prolog command-line.
% everyone is a keyword for a rule. If the list doesn't contain 'everyone'
% it's a fact.
% [men,are,stupid]
% should become ...
stupid(men).
% [everyone,who,is,stupid,is,tall]
% should become ...
tall(X) :- stupid(X).
% [everyone,who,is,not,tall,is,green]
% should become ...
green(X) :- not(tall(X)).
% Therefore, this query should return true/yes:
?- green(women).
true.
I don't need anything super fancy for this as my input will always follow a couple of rules and therefore just needs to be analyzed according to these rules.
I've been thinking about this for probably an hour now, but didn't come to anything even considerable, so I can't provide you with what I've tried so far. Can anyone push me into the right direction?
Consider using a DCG. For example:
list_clause(List, Clause) :-
phrase(clause_(Clause), List).
clause_(Fact) --> [X,are,Y], { Fact =.. [Y,X] }.
clause_(Head :- Body) --> [everyone,who,is,B,is,A],
{ Head =.. [A,X], Body =.. [B,X] }.
Examples:
?- list_clause([men,are,stupid], Clause).
Clause = stupid(men).
?- list_clause([everyone,who,is,stupid,is,tall], Clause).
Clause = tall(_G2763):-stupid(_G2763).
I leave the remaining example as an easy exercise.
You can use assertz/1 to assert such clauses dynamically:
?- List = <your list>, list_clause(List, Clause), assertz(Clause).
First of all, you could already during the tokenization step make terms instead of lists, and even directly assert rules into the database. Let's take the "men are stupid" example.
You want to write down something like:
?- assert_rule_from_sentence("Men are stupid.").
and end up with a rule of the form stupid(men).
assert_rule_from_sentence(Sentence) :-
phrase(sentence_to_database, Sentence).
sentence_to_database -->
subject(Subject), " ",
"are", " ",
object(Object), " ",
{ Rule =.. [Object, Subject],
assertz(Rule)
}.
(let's assume you know how to write the DCGs for subject and object)
This is it! Of course, your sentence_to_database//0 will need to have more clauses, or use helper clauses and predicates, but this is at least a start.
As #mat says, it is cleaner to first tokenize and then deal with the tokenized sentence. But then, it would go something like this:
tokenize_sentence(be(Subject, Object)) -->
subject(Subject), space,
be, !,
object(Object), end.
(now you also need to probably define what a space and an end of sentence is...)
be -->
"is".
be -->
"are".
assert_tokenized(be(Subject, Object)) :-
Fact =.. [Object, Subject],
assertz(Fact).
The main reason for doing it this way is that you know during the tokenization what sort of sentence you have: subject - verb - object, or subject - modifier - object - modifier etc, and you can use this information to write your assert_tokenized/1 in a more explicit way.
Definite Clause Grammars are Prolog's go-to tool for translating from strings (such as your English sentences) to Prolog terms (such as the Prolog clauses you want to generate), or the other way around. Here are two introductions I'd recommend:
http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse29
http://www.pathwayslms.com/swipltuts/dcg/

Illegal argument to format sequence

I'm trying to write a getline predicate in Prolog that will grab an entire line. I'm running into problems when trying to use sformat to append strings:
getline(_, Str, 10) :-
format('NL. String: ~s\n', [Str]).
getline(_, Str, -1) :-
format('EOF. String: ~s\n', [Str]).
getline(InStr, Str, Cd) :-
sformat(NewString, '~s~c', [Str, Cd]),
get_code(InStr, C),
getline(InStr, NewString, C).
getline(InStr, Str) :-
get_code(InStr, C),
getline(InStr, Str, C).
test(InFile) :-
open(InFile, read, InStr),
getline(InStr, Line).
running the goal test("in.txt"). with the above gets me this error:
ERROR: Prolog initialisation failed:
ERROR: format/3: Illegal argument to format sequence ~s: _G940
It seems like _G940 is some sort of pointer, but I'm not sure where to go from there. Any help would be appreciated!
EDIT: Now it kinda works (i.e. no errors), but still leaves the problem of getting the complete string back up to the caller without causing the same problem again:
getline(_, Strn, 10) :-
format('NL. String: ~s\n', [Strn]).
getline(_, Strn, -1) :-
format('EOF. String: ~s\n', [Strn]).
getline(InStrm, Strn, Cd) :-
format(string(NewString), '~s~c', [Strn, Cd]),
get_code(InStrm, C),
getline(InStrm, NewString, C).
getline(InStrm) :-
get_code(InStrm, Cd),
getline(InStrm, '', Cd).
test(InFile) :-
open(InFile, read, InStrm),
getline(InStrm).
I have adapted your code a bit so that it now (1) reads the first line from the given text file, and (2) returns the first line of that text file in a string to the caller.
:- use_module(library(readutil)).
test(File, String):-
setup_call_cleanup(
open(File, read, In),
read_line_to_string(In, String),
close(In)
).
Notice that I use library readutil to do part of the job
Also, I use setup_call_cleanup/3 to make sure the stream gets closed in case reading a line fails or throws an exception.
Hope this helps!

Determine the type of characters

I would like to determine in Prolog the type of a string of characters, if it is alphabetic, alphanumeric or numeric.
For example:
"I use this page" alphabetic
"0c0d24e" alphanumeric
How can i do?
the predicate available is char_type/2, or better, code_type/2.
To apply to each code in string, use maplist/2. The only problem it's the wrong arguments order of code_type. Then a service predicate is needed (or download lambda, if you're using SWI-Prolog, with ?- pack_install(lambda).).
Without lambda:
code_type_(X,Y) :- code_type(Y,X).
?- maplist(code_type_(alpha), "abc").
true.
With lambda:
?- [library(lambda)].
?- maplist(\C^code_type(C,alpha), "abc").
true.
edit after comments, it's apparent that more flexible parsing is required. A DCG it's the recommended way to go: library(dcg/basics) offers some prebuilt 'categorizer', and highlights the proper way to write your own, combining with code_type: for instance, here is a recently added rule:
%% prolog_var_name(-Name:atom)// is semidet.
%
% Matches a Prolog variable name. Primarily intended to deal with
% quasi quotations that embed Prolog variables.
prolog_var_name(Name) -->
[C0], { code_type(C0, prolog_var_start) }, !,
prolog_id_cont(CL),
{ atom_codes(Name, [C0|CL]) }.
prolog_id_cont([H|T]) -->
[H], { code_type(H, prolog_identifier_continue) }, !,
prolog_id_cont(T).
prolog_id_cont([]) --> "".
see how code_type/2 is used to qualify single characters...
more edit - note: untested
qualify_atom(Atom, Type) :-
atom_codes(Atom, Codes),
qualify_codes(Codes, Type).
qualify_codes(Codes, Type) :-
( maplist(code_type_(alnum), Codes)
-> Type = alnum
; maplist(code_type_(alpha), Codes)
-> Type = alpha
; Type = unknown
).
then, to work on a list
?- maplist(qualify_atom, Atoms, Types).
edit
An update of this answer is mandatory: since library(yall) has been released in SWI-Prolog, and is autoloaded, we can now write:
?- maplist([C]>>code_type(C,alpha), `abc`).
Also, note the change in literal representation: double quotes in SWI-Prolog ver.7+ don't represent anymore a list of character codes.

Prolog, how to show multiple output in write()

go :- match(Mn,Fn),
write('--Matching Result--'),
nl,
write(Mn),
write(' match with '),
write(Fn),
match(Mn1,Fn1).
person(may,female,25,blue).
person(rose,female,20,blue).
person(hock,male,30,blue).
person(ali,male,24,blue).
match(Mn,Fn):-person(Fn,'female',Fage,Fatt),
person(Mn,'male',Mage,Matt),
Mage>=Fage,
Fatt=Matt.
Hi,this is my code...but it's only can show the 1 output...but there are 3 pair of matching in match(X,Y).how to show them all in my go function.
Thank you
You get all your matches if you force backtracking, usually by entering ; (e.g. in SWI Prolog). But you also see that you are getting unnecessary outputs true. This is because the last clause in go is match(Mn1,Fn1). This clause succeeds three times and binds the variables Mn1,Fn1 but then only true is output, because you do not write() after that clause. The fourth time match(Mn1,Fn1) fails and by backtracking you come back to the first clause match(Mn,Fn) that matches, the match is output, etc.
You surely do not want to have this behavior. You should remove the last clause match(Mn1,Fn1) in go. Now by pressing ; you get the 3 matches without any output true in between.
But what you likely want is that the program does the backtracking. To achieve this, you just need to force backtracking by adding false as the last clause. To get proper formatting of the output, use the following program. The last clause go2. is added to get true at the very end.
go2 :- write('--Matching Result--'), nl,
match(Mn,Fn),
write(Mn), write(' match with '), write(Fn), nl,
fail.
go2.
This technique is called failure driven loop.
If you have any predicate that has multiple results and want to to find all of them, you should use findall/3
For example, in your case, you could do something like:
findall([X,Y], match(X,Y),L).
L will be a list that will contain all the X,Y that satisfy match(X,Y) in the format [X,Y].
for example, assuming that:
match(m1,f1).
match(m2,f2).
the result will be L = [ [m1,f1], [m2,f2] ]
note that you can define the format as you wish, for example you could write:
findall(pair(X,Y), match(X,Y), L).
L = [ pair(m1,f1), pair(m2,f2) ]
findall( X, match(X,Y), L).
L = [ m1, m2]
findall( 42, match(X,Y), L).
L = [42, 42]
then you have to recurse on the list to print them.
However, if you wish to find one result, run some code and then continue you could use forall/2:
forall(match(X,Y), my_print(X,Y).
Prolog is a lazy language. Which means that it will stop once it has found a condition that made your problem true. This will be the very first match alone.
IF your code is working (I haven't tried it), then you should try and run the match-statement like this in your prolog inspector: match(X,Y)
The prolog inspector will return all states and print them for you.

Resources