Alternative to string_upper/2 - prolog

Machine, which my code is supposed to work on, for some reason doesn't know predicate string_upper/2. Are there any alternatives? Eventually, what would code for this predicate look like?

The example you gave in the comments is actually not a string but a pair of atoms. Since you already opted to use upcase_atom/2, in the code you deleted from your post, I'd point out that this built-in is only working in one direction, that is if the the first argument is atomic. Consider the following queries:
?- upcase_atom(bo,'BO'). % Is 'BO' the uppercase version of bo?
true. % Yes, it is.
?- upcase_atom(bo,B). % What's the uppercase version of bo?
B = 'BO'. % It's 'BO'
?- upcase_atom(B,'BO'). % What atom has 'BO' as uppercase version?
ERROR: upcase_atom/2: Arguments are not sufficiently instantiated
?- upcase_atom(B,1). % What atom has 1 as uppercase version?
ERROR: upcase_atom/2: Arguments are not sufficiently instantiated
Your example query, from your deleted code, is ?- divideIt(a-b).. The atom a is unified with the variable V in the head of the rule divideIt/1. Then Prolog calls the first goal upcase_atom(V,ve) that has to fail because the atom ve is not the uppercase version of the atom a. Consequently your example query fails as well. If you apply the following minor changes to your predicate, it yields the desired result:
divideIt(V-W) :-
upcase_atom(V,Ve), % <- Ve (=variable) instead of ve (=atom)
write(Ve),
write('-'), % <- '-' instead of "-"
upcase_atom(W,We), % <- We (=variable) instead of we (=atom)
write(We).
?- divideIt(a-b).
A-B
true.

Break the string into a list of character codes. Then convert those codes into the appropriate upper case code if necessary before reconstructing the string. A more complete answer would require knowing which prolog you're using, its available predicates & how it represents strings.
You might want to read up on different Prolog's handling of characters, Unicode and strings. For this, the SWI-Prolog implementation has some good documentation:
How traditional(Edinburgh), ISO & SWI prolog handles characters: 4.2 Character Representation
How ISO & SWI prologs support Unicode 2.16.1.8 Unicode Prolog source
How SWI has a specific string-type object which uses quotes to denote them: 5.2 The string type and its double quoted syntax

Related

Prolog type errors with DCG library functions

I'm trying to write a DCG for a command interface. The idea is to read a string of input, split it on spaces, and hand the resulting list of tokens to a DCG to parse it into a command and arguments. The result of parsing should be a list of terms which I can use with =.. to construct a goal to call. However, I've become really confused by the string type situation in SWI-Prolog (ver. 7.2.3). SWI-Prolog includes a library of basic DCG functionality, including a goal integer//1 which is supposed to parse an integer. It fails due to a type error, but the bigger problem is that I can't figure out how to make a DCG work nicely in SWI-Prolog with "lists of tokens".
Here's what I'm trying to do:
:- use_module(library(dcg/basics)).
% integer//1 is from the dcg/basics lib
amount(X) --> integer(X), { X > 0 }.
cmd([show,all]) --> ["show"],["all"].
cmd([show,false]) --> ["show"].
cmd([skip,X]) --> ["skip"], amount(X).
% now in the interpreter:
?- phrase(cmd(L), ["show","all"]).
L = [show, all].
% what is the problem with this next query?
?- phrase(cmd(L), ["skip", "50"]).
ERROR: code_type/2: Type error: `character' expected, found `"50"' (a string)
I have read Section 5.2 of the SWI manual, but it didn't quite answer my questions:
What type is expected by integer//1 in the dcg/basics library? The error message says "character", but I can't find any useful reference as to what exactly this means and how to provide it with "proper" input.
How do I pass a list of strings (tokens) to phrase/2 such that I can use integer//1 to parse a token as an integer?
If there's no way to use the integer//1 primitive to parse a string of digits into an integer, how should I accomplish this?
I did quite a bit of expermenting with using different values for the double_quote flag in SWI-Prolog, plus different input formats, such as using a list of atoms, using a single string as the input, i.e. "skip 50" rather than ["skip", "50"], and so on, but I feel like there are assumptions about how DCGs work that I don't understand.
I have studied these three pages as well, which have lots of examples but none quite address my issues (some links omitted since I don't have enough reputation to post all of them):
The tutorial "Using Definite Clause Grammars in SWI-Prolog" by Anne Ogborn
A tutorial from Amzi! Prolog about writing command interfaces as DCGs.
Section 7.3 of J. R. Fisher's Prolog tutorial
A third, more broad question is how to generate an error message if an integer is expected but cannot be parsed as one, something like this:
% the user types:
> skip 50x
I didn't understand that number.
One approach is to set the variable X in the DCG above to some kind of error value and then check for that later (like in the hypothetical skip/1 goal that is supposed to get called by the command), but perhaps there's a better/more idiomatic way? Most of my experience in writing parsers comes from using Haskell's Parsec and Attoparsec libraries, which are fairly declarative but work somewhat differently, especially as regards error handling.
Prolog doesn't have strings. The traditional representation of a double quoted character sequence is a list of codes (integers). For efficiency reasons, SWI-Prolog ver. >= 7 introduced strings as new atomic data type:
?- atomic("a string").
true.
and backquoted literals have now the role previously held by strings:
?- X=`123`.
X = [49, 50, 51].
Needless to say, this caused some confusion, also given the weakly typed nature of Prolog...
Anyway, a DCG still works on (difference) lists of character codes, just the translator has been extended to accept strings as terminals. Your code could be
cmd([show,all]) --> whites,"show",whites,"all",blanks_to_nl.
cmd([show,false]) --> whites,"show",blanks_to_nl.
cmd([skip,X]) --> whites,"skip",whites,amount(X),blanks_to_nl.
and can be called like
?- phrase(cmd(C), ` skip 2300 `).
C = [skip, 2300].
edit
how to generate an error message if an integer is expected
I would try:
...
cmd([skip,X]) --> whites,"skip",whites,amount(X).
% integer//1 is from the dcg/basics lib
amount(X) --> integer(X), { X > 0 }, blanks_to_nl, !.
amount(unknown) --> string(S), eos, {print_message(error, invalid_int_arg(S))}.
prolog:message(invalid_int_arg(_)) --> ['I didn\'t understand that number.'].
test:
?- phrase(cmd(C), ` skip 2300x `).
ERROR: I didn't understand that number.
C = [skip, unknown] ;
false.

Convert a char list to a string in Prolog

I'm trying to convert a char array to a string, and then convert this string a list. This is what I want:
code_list=[97 97]
string_s="aa"
string_list=[aa]
I'm not sure about notation, whether I used them correctly or not.
A few examples that may help you understand the different ways to represent "strings" in SWI-Prolog, and convert from one representation to another (note that Prolog doesn't really have types, so this is not a type converstion).
This is all for SWI-7 and later:
$ swipl --version
SWI-Prolog version 7.1.27 for x86_64-linux
Now, from the top level:
?- atom(foo).
true.
?- string("foo").
true.
?- is_list(`foo`).
true.
?- is_list([f,o,o]).
true.
?- atom_chars(A, [f,o,o]).
A = foo.
?- atom_codes(foo, Codes).
Codes = [102, 111, 111].
?- string_codes(Str, `foo`).
Str = "foo".
?- write(`foo`).
[102,111,111]
true.
?- string_chars(Str, [f,o,o]).
Str = "foo".
You should read the documentation on the used predicates if you want to learn a bit more.
Commonly, Prolog systems do not support a separate string data type. Instead, there are atoms, lists of codes, and lists of characters which serve the very purpose of strings. So if you are learning Prolog, stick to those types. The following query works in any Prolog system:
?- Code_list = [97, 97], atom_codes(Atom, Code_list), atom_chars(Atom, Chars).
Code_list = [97,97], Atom = aa, Chars = [a,a].
This should answer your question.
For completeness, remark that strings as a separate data type are very rare. And most often they are not a valid extension to the ISO Prolog standard.
SWI7 has particularly odd behavior with respect to strings. The first thing to try is the following query:
?- phrase("abc","abc").
This query should succeed, otherwise learning DCGs (one of Prolog's main features) will be very cumbersome to you. In SWI7 the default is:
?- phrase("abc","abc").
ERROR: Type error: `list' expected, found `"abc"' (a string)
So the double quote notation means two different things. To get consistent behavior, you have several options.
Install SWI Prolog 6, or another system like GNU or SICStus Prolog.
Call SWI with swipl --traditional which fixes many of the incompatibilities.
Change the Prolog flag double_quotes to codes or chars. See this answer how this is done.
The proper way to express your example would be:
Code_list = [97, 97]
String_s = "aa"
(I'm not quite sure what you mean by the last line.)

Converting 1st letter of atom in Prolog

How to convert 1st letter of an atom in uppercase letter in LPA prolog? the only way I know is using 'lwrupr'. But it capitalizes all the letters.
I don't have LPA Prolog available, but here is an educated guess, resulting from a quick read of reference manual: take the first letter from the atom, make it upper case, and rebuild the word
first_char_uppercase(WordLC, WordUC) :-
atom_chars(WordLC, [FirstChLow|LWordLC]),
atom_chars(FirstLow, [FirstChLow]),
lwrupr(FirstLow, FirstUpp),
atom_chars(FirstUpp, [FirstChUpp]),
atom_chars(WordUC, [FirstChUpp|LWordLC]).
In SWI-Prolog, we can test it defining the missing builtin lwrupr/2 like this
lwrupr(Low, Upp) :- upcase_atom(Low, Upp).
and we get
?- first_char_uppercase(carlo,X).
X = 'Carlo'.
?- first_char_uppercase('Carlo',X).
X = 'Carlo'.

Check if any element in List begins with a specific character

I've a list of words, for example [cola,fanta,pepsi] and I want to write a predicate that checks if any of the elements begins with the character specified.
My code so far is as follows:
chk_first_letter(Char,[]):-fail.
chk_first_letter(Char, [H|T]):-
perform_check(Char, H);
chk_first_letter(Char, T).
perform_check(Char,[First|_]):-memberchk(Char, First).
However consulting my file and calling chk_first_letter(p,[cola,fanta,pepsi]) gives me no even if pepsi begins with a p.
I've tried with Char==First instead of memberchk(Char,First) but it didn't work either. I'm not sure about the difference.
You have a list of atoms, and your perform_check/2 compares two atoms. An atom is not a list of characters! You need to use atom processing, for example:
perform_check(First, Word) :-
sub_atom(Word, 0, 1, _After, First).
http://gprolog.univ-paris1.fr/manual/html_node/gprolog043.html#sec200
There are a bunch of other built-ins in this section that could be used, for example for breaking the atom into characters or character codes (atom_chars/2 and atom_codes/2). But what sub_atom/5 also allows you to do easily:
prefixes of any length:
sub_atom(Word, 0, _Length, _After, Prefix).
suffixes:
sub_atom(Word, _Before, _Length, 0, Suffix).
First attempt:
chk_first_letter(Char, Atoms) :- member(A, Atoms), atom_chars(A, [Char|_]).
atom_chars/2 it's an ISO predicate.
Your code it's almost working, can be simplified this way:
chk_first_letter(Char, [H|T]):-
atom_chars(H, [Char|_]);
chk_first_letter(Char, T).
memberchk expects to be called with a list as the second argument. In your case, you're providing it with a single character.
And then you can probably do away with it altogether by taking advantage of unification:
perform_check(Char,[Char|_]).
This assumes your string type is a list of characters (whatever the character format). If you intend to operate on atoms directly, you could do it this way instead:
perform_check(Char,String) :- atom_concat(Char,_,String)
There would be a few more steps to make your code more idiomatic, but this seems to be the actual wrong part of it.

Problem when reading backslash in Prolog

I'm writing a lexer in Prolog which will be used as a part of functional language interpreter. Language spec allows expressions like for example let \x = x + 2; to occur. What I want lexer to do for such input is to "return":
[tokLet, tokLambda, tokVar(x), tokEq, tokVar(x), tokPlus, tokNumber(2), tokSColon]
and the problem is, that Prolog seems to ignore the \ character and "returns" the line written above except for tokLambda.
One approach to solve this would be to somehow add second backslash before/after every occurrence of one in the program code (because everything works fine if I change the original input to let \\x = x + 2;) but I don't really like it.
Any ideas?
EDIT:
If anyone should have similar problems, that's how I solved it:
main(File) :-
open(File,read,Stream),
read_stream_to_codes(Stream, Codes),
lexer(X,Codes,[]),
... invoke other methods
Where did you get the string let \x = x + 2; from?
If it is in your Prolog program: yes, you have to double the backslashes.
If it is from an external file: How do you read it from there? Maybe that predicate is interpreting the backslash specially.
I got inspired by that problem and wrote a bit of code, which should be portable to all Prolog implementations:
% readline(-Line)
%
% Reads one line from the current input. The line is then returned as a list
% of one-character atoms, excluding the newline character.
% The returned line doesn't tell you whether the end of input has been reached
% or not.
readline(Line) :-
'readline:read'([], Reversed),
reverse(Line, Reversed).
'readline:read'(Current, Out) :-
get_char(C), 'readline:append'(C, Current, Out).
'readline:append'(-1, Current, Current) :- !.
'readline:append'('\n', Current, Current) :- !.
'readline:append'(C, Current, Line) :-
'readline:read'([C | Current], Line).
I tried it, and it worked for me.
Of course, as explained in question 1846199, you can also use read_line_to_codes/2.

Resources