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.