Determine the type of characters - prolog

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.

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.

Writing a predicate to add atoms

I have to write a predicate to do work like following:
?- cat(north,south,X).
X = northsouth
?- cat(alley,'91',Y).
X = alley91
?-cat(7,uthah,H).
Bad Input
H = H
Please Help..
atom_concat_redefined(A1, A2, A3) :-
( nonvar(A1) -> atom_chars(A1, Chs1) ; true ),
( nonvar(A2) -> atom_chars(A2, Chs2) ; true ),
( nonvar(A1), nonvar(A2) -> true ; atom_chars(A3, Chs3) ),
append(Chs1, Chs2, Chs3),
atom_chars(A1, Chs1),
atom_chars(A2, Chs2),
atom_chars(A3, Chs3).
This definition produces the same errors in a standard conforming implementation like SICStus or GNU - there should be no other differences, apart from performance. To compare the errors use the goal:
?- catch(atom_concat_redefined(A,B,abc+1), error(E,_), true).
E = type_error(atom,abc+1).
Note the underscore in error(E,_), which hides the implementation defined differences. Implementations provide additional information in this argument, in particular, they would reveal that atom_chars/2 or atom_concat/3 produced the error.
atom_codes/2 it's the ISO approved predicate to convert between an atom and a list of codes. When you have 2 lists corresponding to first two arguments, append/3 (alas, not ISO approved, but AFAIK available in every Prolog), will get the list corresponding to third argument, then, convert that list to atom...
Note that, while append/3 is a 'pure' Prolog predicate, and can work with any instantiation pattern, atom_codes/2 requires at least one of it's argument instantiated. Here is a SWI-Prolog implementation of cat/3, 'working' a bit more generally. I hope it will inspire you to read more about Prolog...
ac(X,Xs) :- when((ground(X);ground(Xs)), atom_codes(X,Xs)).
cat(X,Y,Z) :- maplist(ac, [X,Y,Z],[Xs,Ys,Zs]), append(Xs,Ys,Zs).
edit
as noted by #false I was wrong about append/3. Now I'll try to understand better what append/3 does... wow, a so simple predicate, so behaviour rich!

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/

How to check that certain strA is the substring of strB using Prolog?

So basically I use this code to check the substring:
substring(X,S) :- append(_,T,S), append(X,_,T), X \= [].
and my input is this:
substring("cmp", Ins) % Ins is "cmp(eax, 4)"
But when I use swi-prolog to trace this code, I find this:
substring([99, 109, 112], cmp(eax, 4))
and obviously it failed...
So could anyone give me some help?
SWI-Prolog has recently changed the traditional string literals as 'list of codes' to a more memory efficient representation (starting from version 7).
As a consequence (among others more difficult to explain), append/3 doesn't work anymore for your task, unless you convert explicitly to list of codes.
Contextually, many builtins have been introduced, like sub_string/5: for instance, try
?- sub_string("cmp(eax, 4)", Start,Len,Stop, "eax").
Start = Stop, Stop = 4,
Len = 3
Make this string a term of the form cmp(eax, 4). Here, in Prolog lingo, you have:
the term cmp(eax, 4)
with a functor cmp/2
with a first argument the atom eax
and a second argument the integer 4
Now that you have a term, you can use pattern matching in the head of your predicate (unification) to write predicates like:
apply_instruction(cmp(Reg, Operand) /*, other arguments as needed */) :-
/* do the comparison of the contents of _Reg_ and the values in _Operand_ */
apply_instruction(add(Reg, Addend) /*, other arguments */) :-
/* add _Addend_ to _Reg_ */
% and so on
How to make a term out of your input: there are many ways, the easiest would be to read one full line (depends on the Prolog implementation you are using, in SWI-Prolog, assuming you have your input stream in In):
read_line_to_codes(In, Line).
and then use a DCG to parse it. A DCG would look maybe something like:
instruction(cmp(Op1, Op2)) -->
"cmp",
ops(Op1, Op2).
instruction(add(Op1, Op2) -->
"add",
ops(Op1, Op2).
ops(Op1, Op2) -->
space,
op1(Op1), optional_space,
",", optional_space,
op2(Op2),
space_to_eol.
% and so on
You can then use phrase/2 to apply the DCG to the line you have read:
phrase(instruction(Instr), Line).

Prolog remove character spaces from atom for using term_to_atom

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]}.

Resources