Find out if first letter is a vowel prolog

2019-07-15 19:17发布

问题:

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

回答1:

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


回答2:

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).



回答3:

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.