I'm used to procedural programming languages, and I'm kind of struggling with prolog - the lack of resources online is also a bummer.
What would be the most 'prolog'-y way to get the first character of a given variable and check if it is a vowel?
Something like this is what I'm after, I think? This is all pseudocode - but is that how you'd solve it?
isVowel(Word) :-
vowels = [a, e, i, o, u],
firstLetter(Word[0]),
(
firstLetter in vowels ->
Vowel!
; Not a vowel!
).
Thanks so much,
Ollie
In Prolog you write definite clauses (rules) for predicates. Predicates describe logical relations. For example, you might have a predicate is_vowel/1 which is true if the given argument is a vowel.
is_vowel(Letter):-
member(Letter, "aeiouAEIOU").
In order to see if a word starts with a vowel you have to take the first letter:
starts_with_vowel(Word):-
Word = [First|_],
is_vowel(First).
Now, you can do unification and pattern matching simultaneously like this:
starts_with_vowel([FirstLetter|_]):-
is_vowel(FirstLetter).
A few example queries:
?- starts_with_vowel("Italy").
true ;
false.
?- starts_with_vowel("Vietnam").
false.
?- Letters = [_|"pple"], starts_with_vowel(Letters), string_to_atom(Letters, Word).
Letters = [97, 112, 112, 108, 101],
Word = apple ;
Letters = [101, 112, 112, 108, 101],
Word = epple ;
Letters = [105, 112, 112, 108, 101],
Word = ipple ...
You've got answers, but:
Don't do member, or memberchk. Instead, just use a table:
vowel(a).
vowel(e).
vowel(i).
vowel(o).
vowel(u).
Then, you don't say what sort of variable you have. If you have an atom:
?- sub_atom(Word, 0, 1, _, First), vowel(First).
You can convert almost anything to an atom easily. See for example here.
This query will succeed if the first character of the atom is a vowel, and fail otherwise. To make it a predicate:
first_letter_vowel(Word) :-
sub_atom(Word, 0, 1, _, First),
vowel(First).
Or, for example:
nth_letter_vowel(N, Word) :-
sub_atom(Word, N, 1, _, Letter),
vowel(Letter).
If you are using SWI-Prolog, you can also use downcase_atom/2:
nth_letter_vowel(N, Word) :-
sub_atom(Word, N, 1, _, Letter),
downcase_atom(Letter, Lower_Case),
vowel(Lower_Case).
EDIT: Why a table of facts and not member/2 or memberchk/2?
It is cleaner; it is more memory efficient, and speedy; it makes the intent of the program obvious; it is (and has been) the preferred way to do it: see the very bottom of this page (which, by the way, discusses many interesting things).
Here are is the exhaustive list of possible queries with vowel/1, when it is defined as a table of facts:
?- vowel(r).
false.
?- vowel(i).
true.
?- vowel(V).
V = a ;
V = e ;
V = i ;
V = o ;
V = u.
?- vowel(foobar(baz)). % or anything, really
false.
Now we know that member/2 is going to leave behind choice points, so memberchk/2 is certainly to be preferred (unless we mean to use the choice points!). But even then:
?- memberchk(a, [a,b,c]).
true. % that's fine
?- memberchk(x, [a,b,c]).
false. % ok
?- memberchk(a, L).
L = [a|_G1190]. % what?
?- memberchk(X, [a,b,c]).
X = a. % what?
So yes, in the context of the original question, assuming we make sure to carefully check the arguments to member/2 or memberchk/2, the reasons to prefer a table of facts are only stylistic and "practical" (memory efficiency, speed).
This can be done in several ways. In this particular solution I use Definite Clause Grammar (DCG).
Also, the answer depends a bit on what a "word" is. If it is a list of character codes, then the following suffices:
starts_with_vowel --> vowel, dcg_end.
vowel --> [X], {memberchk(X, [0'a,0'A,0'e,0'E,0'i,0'I,0'o,0'O,0'u,0'U])}.
dcg_end(_, []).
Example of use:
?- phrase(starts_with_vowel, `answer`).
true.
?- phrase(starts_with_vowel, `question`).
false.
PS: Notice that the use of backquotes here is SWI7-specific. In other Prologs a list of codes would appear within double quotes.
If a word is something else, then you first need to convert to codes. E.g., atom_codes(answer, Codes) if a word is represented by an atom.
Related
I am very new to prolog, so assume that I know very little terminology.
I am using swipl in SWI-prolog.
I want to check if a string starts with a left squiggly bracket('{') and ends with a right squiggly bracket('}'}
Some answers that I have read online have lead me to program the following into my knowledge base to check if the string starts with a left squiggly bracket.
start_left_squiggle([Letter|_]):-
Letter = '{'.
But when I run this function, I get false, when I expect it to return true.
?- start_left_squiggle('{hello').
false.
As well, answers that seem correct for checking the if the last character is a squiggly bracket have lead me to code the following.
last_char(str, X):-
name(S, N),
reverse(N, [F|_]),
name(X, [F]).
end_right_squiggle(Werd):-
last_char(Werd, Last),
Last = '}'.
And I again get false when running the function, when I expect it to return true.
?- end_right_squiggle('hello}').
false.
Use sub_atom(Atom, Before, Length, After, Subatom) like so:
?- sub_atom('{abc}',0,1,_,C).
C = '{'.
?- sub_atom('{abc}',_,1,0,C).
C = '}'.
Or just test:
?- sub_atom('{abc}',0,1,_,'{').
true.
?- sub_atom('{abc}',_,1,0,'}').
true.
First thing you need to do is to break the atom into list of characters like this:
start_with_left(H):-
atom_chars(H,X), %here x is a list
X = [L|_], %get the head of the list which is frist element an compare
L == '{'.
You can use a recursive definition to check righ side of the atom after converting the atom into list of characters and when length of the list is 1 then compare it with bracket , it means if last element is same you should get true otherwise False.
Right is like this, it's same but we need last element so we have to use recursion:
start_with_right(H):-
atom_chars(H,X), %here x is a list
length(X,Y),
check_at_end(X,Y).
check_at_end([H|_],1):-
H == '}'.
check_at_end([_|T],Y):-
NewY is Y -1,
check_at_end(T,NewY).
.
I'm trying to wrap my brain around Prolog for the first time (SWI-Prolog) and I'm struggling with what I'm sure are the basics. I'm trying to take a string such as "pie" and print out the military NATO spelling of it to look something like this:
spellWord("Pie").
Papa
India
Echo
Currently I'm just trying to verify that I'm using the [H|T] syntax and Write function correctly. My function is:
spellWord(String) :- String = [H|T], writeChar(H), spellWord(T).
writeChar(String) :- H == "P", print4("Papa").
When making a call to spellWord("Pie"). this currently just returns false.
SWI-Prolog has several different representation of what you might call "strings".
List of character codes (Unicode);
List of chars (one-letter atoms);
Strings, which are "atomic" objects, and can be manipulated only with the built-in predicates for strings;
And finally, of course, atoms.
You should read the documentation, but for now, you have at least two choices.
Choice 1: Use a flag to make double-quoted strings code lists
$ swipl --traditional
Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.19-57-g9d8aa27)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.
For help, use ?- help(Topic). or ?- apropos(Word).
?- X = "abc".
X = [97, 98, 99].
At this point, your approach should work, as you now have a list.
Choice 2: Use the new code list syntax with back-ticks
?- X = `abc`.
X = [97, 98, 99].
And, of course, there are predicates that convert between atoms, code lists, char lists, and strings. So, to make a list of chars (one-character atoms), you have:
atom_chars/2
char_code/2
string_chars/2
As for your predicate definition, consider using unification in the head. Also, don't mix side effects (printing) with what the predicate does. Let the top level (the Prolog interpreter) do the printing for you.
nato(p, 'Papa').
nato(i, 'India').
nato(e, 'Echo').
% and so on
word_nato([], []).
word_nato([C|Cs], [N|Ns]) :-
char_code(Char, C),
char_type(U, to_lower(Char)),
nato(U, N),
word_nato(Cs, Ns).
And with this:
?- word_nato(`Pie`, Nato).
Nato = ['Papa', 'India', 'Echo'].
I used chars (one-letter atoms) instead of character codes because those are easier to write.
And finally, you can use the following flag, and set_prolog_flag/2 at run time to change how Prolog treats a string enclosed in double quotes.
For example:
$ swipl
Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.19-40-g2bcbced)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.
For help, use ?- help(Topic). or ?- apropos(Word).
?- current_prolog_flag(double_quotes, DQs).
DQs = string.
?- string("foo").
true.
?- set_prolog_flag(double_quotes, codes).
true.
?- X = "foo".
X = [102, 111, 111].
?- set_prolog_flag(double_quotes, chars).
true.
?- X = "foo".
X = [f, o, o].
?- set_prolog_flag(double_quotes, atom).
true.
?- X = "foo".
X = foo.
Regardless of the Prolog system you are using and unless you have to
maintain existing code, stick to set_prolog_flag(double_quotes, chars). This works in many
systems
like B, GNU, IF, IV, Minerva, Scryer, SICStus, SWI, Tau, Trealla, YAP. So it is a safe
bet. The other options mentioned by #Boris are hard to debug. One is even specific to SWI
only.
?- set_prolog_flag(double_quotes, chars).
true.
?- L = "abc".
L = [a,b,c].
With
library(double_quotes)
these strings can be printed more compactly.
In SWI, the best you can do is to put in your .swiplrc the lines:
:- set_prolog_flag(back_quotes, string).
:- set_prolog_flag(double_quotes, chars).
:- use_module(library(double_quotes)).
For your concrete example, it is a good idea to avoid producing
side-effects immediately. Instead consider defining a relation
between a word and the spelling:
word_spelling(Ws, Ys) :-
phrase(natospelling(Ws), Ys).
natospelling([]).
natospelling([C|Cs]) -->
{char_lower(C, L)},
nato(L),
"\n",
natospelling(Cs).
nato(p) --> "Papa".
nato(i) --> "India".
nato(e) --> "Echo".
char_lower(C, L) :-
char_type(L, to_lower(C)).
?- word_spelling("Pie",Xs).
Xs = "Papa\nIndia\nEcho\n".
?- word_spelling("Pie",Xs), format("~s",[Xs]).
Papa
India
Echo
Xs = "Papa\nIndia\nEcho\n".
And here is your original definition. Most of the time, however, rather stick with the pure core of it.
spellWord(Ws) :-
word_spelling(Ws, Xs),
format("~s", [Xs]).
Also note that SWI's built-in library(pio) only works for
codes and leaves unnecessary choice-points open. Instead, use this
replacement
which works for chars and codes depending on the Prolog flag.
Historically, characters were first represented as atoms of length
one. That is, 1972 in Prolog 0. However, there, strings were
represented in a left-associative manner which facilitated suffix matching.
plur(nil-c-i-e-l, nil-c-i-e-u-x).
Starting with Prolog I, 1973, double quotes meant a list of characters
like today.
In 1977, DECsystem 10 Prolog changed the meaning of double quotes
to lists of characters codes and used codes in place of chars. This made some I/O operations
a little bit more efficient, but made debugging such programs much
more difficult [76,105,107,101,32,116,104,105,115] - can you read it?
ISO Prolog supports both. There is a flag double_quotes that
indicates how double quotes are
interpreted. Also,
character related built-ins are present for both:
char_code/2
atom_chars/2, number_chars/2, get_char/1/2, peek_char/1/2, put_char/1/2
atom_codes/2, number_codes/2, get_code/1/2, peek_code/1/2, put_code/1/2
The problems with your code are:
spellWord(String) :- String = [H|T], writeChar(H), spellWord(T).
When you give this predicate a long string, it will invoke itself with the tail of that string. But when String is empty, it cannot be split into [H|T], therefore the predicate fails, returning false.
To fix this, you have to define additionally:
spellWord([]).
This is the short form of:
spellWord(String) :- String = [].
Your other predicate also has a problem:
writeChar(String) :- H == "P", print4("Papa").
You have two variables here, String and H. These variables are in no way related. So no matter what you pass as a parameter, it will not influence the H that you use for comparison. And since the == operator only does a comparison, without unification, writeChar fails at this point, returning false. This is the reason why there is no output at all.
I have a list like [apple, orange]and I want to convert it to a string like "apple,orange" in Prolog. Do you have any idea?
In SWI-Prolog you can simply use atomic_list_concat/3 and atom_string/2:
?- atomic_list_concat([apple, banana, oranges], ',', Atom), atom_string(Atom, String).
Atom = 'apple,banana,oranges',
String = "apple,banana,oranges".
In SWI-Prolog you can use with_output_to/2. Below are two versions, one using write/1 and the other writeq/1. It's not clear from your question what kind of behaviour your require.
?- List = [apple, 'ora\\nge'], with_output_to(codes(Codes), write(List)),
format("~s", [Codes]).
[apple,ora\nge]
List = [apple, 'ora\\nge'],
Codes = [91, 97, 112, 112, 108, 101, 44, 111, 114|...].
?- List = [apple, 'ora\\nge'], with_output_to(codes(Codes), writeq(List)),
format("~s", [Codes]).
[apple,'ora\\nge']
List = [apple, 'ora\\nge'],
Codes = [91, 97, 112, 112, 108, 101, 44, 39, 111|...].
if you use swi-prolog you can also do the following:
1) use the string_to_list/2 (you can give a list and get a string or give a string and get a list). the problem is that it wont insert commas so you will have to insert commas between the elements of your list manually; something like
insert_commas([],[]).
insert_commas([H|T],[H,', '|TC]):-
insert_commas(T,TC).
so your predicate would be something like:
list_string_with_commas(L,S):-
insert_commas(L,LC),
string_to_list(S,LC).
2)you can use swritef/3 and string_concat/3. swritef/3 works like writef/2 but instead of writing in the output it creates a string with the data.
list_string_with_commas([],"").
list_string_with_commas([H,T],S):-
swritef(SH,"%t, ",[H]),
list_string_with_commas(T,ST),
string_concat(SH,ST,T).
you might want to do some tail recursion optimisations
I am a Prolog novice - but this is what I came up with.
list_codes([], "").
list_codes([Atom], Codes) :- atom_codes(Atom, Codes).
list_codes([Atom|ListTail], Codes) :-
atom_codes(Atom, AtomCodes),
append(AtomCodes, ",", AtomCodesWithComma),
append(AtomCodesWithComma, ListTailCodes, Codes),
list_codes(ListTail, ListTailCodes).
list_string(List, String) :-
ground(List),
list_codes(List, Codes),
atom_codes(String, Codes).
list_string(List, String) :-
ground(String),
atom_codes(String, Codes),
list_codes(List, Codes).
which gives:
?- list_string([], S).
S = '' .
?- list_string([apple], S).
S = apple .
?- list_string([apple, orange], S).
S = 'apple,orange' .
?- list_string([apple, orange, peach], S).
S = 'apple,orange,peach' .
and:
?- list_string(L, '').
L = [] .
?- list_string(L, 'banana').
L = [banana] .
?- list_string(L, 'banana,litchi').
L = ['banana,litchi'] .
term_string (30 char min S.O.)
Sorry, don't have enough rep to add this comment to Gavin Lock's answer. Gavin's solution is good so far as it goes, but it should be tweaked to avoid giving duplicate answers (or, rather, a variation on the same answer). The problem is that a list with a single atom will unify to two clauses in list_codes/2: the second AND the third clause. (A list with a single element still has a tail, i.e. the empty list.) I believe only the second clause binding is desired. The third clause seems to be designed to be recursive for lists that still have at least two elements. Therefore I recommend changing this clause to be:
list_codes([Atom,Next|ListTail], Codes) :-
atom_codes(Atom, AtomCodes),
list_codes([Next|ListTail], ListTailCodes),
append(AtomCodes, ",", AtomCodesWithComma),
append(AtomCodesWithComma, ListTailCodes, Codes).
This will only unify when the list has at least two elements, and will prevent getting two solutions.
I picked up J a few weeks ago, about the same time the CodeGolf.SE beta opened to the public.
A recurrent issue (of mine) when using J over there is reformatting input and output to fit the problem specifications. So I tend to use code like this:
( ] ` ('_'"0) ) #. (= & '-')
This one untested for various reasons (edit me if wrong); intended meaning is "convert - to _". Also come up frequently: convert newlines to spaces (and converse), merge numbers with j, change brackets.
This takes up quite a few characters, and is not that convenient to integrate to the rest of the program.
Is there any other way to proceed with this? Preferably shorter, but I'm happy to learn anything else if it's got other advantages. Also, a solution with an implied functional obverse would relieve a lot.
It sometimes goes against the nature of code golf to use library methods, but in the string library, the charsub method is pretty useful:
'_-' charsub '_123'
-123
('_-', LF, ' ') charsub '_123', LF, '_stuff'
-123 -stuff
rplc is generally short for simple replacements:
'Test123' rplc 'e';'3'
T3st123
Amend m} is very short for special cases:
'*' 0} 'aaaa'
*aaa
'*' 0 2} 'aaaa'
*a*a
'*&' 0 2} 'aaaa'
*a&a
but becomes messy when the list has to be a verb:
b =: 'abcbdebf'
'L' (]g) } b
aLcLdeLf
where g has to be something like g =: ('b' E. ]) # ('b' E. ]) * [: i. #.
There are a lot of other "tricks" that work on a case by case basis. Example from the manual:
To replace lowercase 'a' through 'f' with uppercase 'A'
through 'F' in a string that contains only 'a' through 'f':
('abcdef' i. y) { 'ABCDEF'
Extending the previous example: to replace lowercase 'a' through
'f' with uppercase 'A' through 'F' leaving other characters unchanged:
(('abcdef' , a.) i. y) { 'ABCDEF' , a.
I've only dealt with the newlines and CSV, rather than the general case of replacement, but here's how I've handled those. I assume Unix line endings (or line endings fixed with toJ) with a final line feed.
Single lines of input: ".{:('1 2 3',LF) (Haven't gotten to use this yet)
Rectangular input: (".;._2) ('1 2 3',LF,'4 5 6',LF)
Ragged input: probably (,;._2) or (<;._2) (Haven't used this yet either.)
One line, comma separated: ".;._1}:',',('1,2,3',LF)
This doesn't replace tr at all, but does help with line endings and other garbage.
You might want to consider using the 8!:2 foreign:
8!:2]_1
-1
I have this fact in my base
fact("name","surname","123").
if i simply write this question:
fact(X,_,_). For X I get some unidentified output.
How can I retrieve any of this values, or how to get this output?
?-fact(X,_,_).
output: name.
Thanks ahead.
In SWI-Prolog you can use string_to_atom/2:
?- assert(fact("name", "surname", "123")).
true.
?- fact(Tmp, _, _), string_to_atom(Tmp, X).
Tmp = [110, 97, 109, 101],
X = name.
Strings in Prolog are enclosed within single quotes. When you use double quotes it means that you want the list of character codes.
?- is_list('abc').
false.
?- is_list("abc").
true.
?- write("abc").
[97,98,99]
true.
?- write('abc').
abc
true.
try this,
| ?- assert(fact("name", "surname", "123")).
yes
| ?- fact(X,_,_).
X = [110,97,109,101];
| ?- fact(_X,_,_),name(Y,_X).
Y = name;