Home > Enterprise >  Incrementing a Counter in Prolog
Incrementing a Counter in Prolog

Time:05-06

This is the current code I have for a problem I am working on. It is supposed to read in from a file, and increment a counter, R, every time it comes across a vowel. Currently, I have it stop when reaching a vowel, but I would like it to increment a counter, then continue processing. Once done, I want it to print R to the console. Thanks in advance!

readWord(InStream, W) :-
        get0(InStream,Char),
        checkChar_readRest(Char,Chars,InStream, R),
        atom_codes(Code,Chars),
        write(Code).

%checkChar_readRest(10,[],_) :- !. % Return
%checkChar_readRest(32,[],_) :- !. % Space
checkChar_readRest(-1,[],_,_) :- !. % End of Stream
checkChar_readRest(97,[],_,R) :- !. % a
checkChar_readRest(101,[],_,R) :- !. % e
checkChar_readRest(105,[],_,R) :- !. % i
checkChar_readRest(111,[],_,R) :- incr(R,R1), write(R1). % o
checkChar_readRest(117,[],_,R) :- !. % u
%checkChar_readRest(end_of_file,[],_,_) :- !.
checkChar_readRest(Char,[Char|Chars],InStream,R) :-
    get0(InStream,NextChar),
    checkChar_readRest(NextChar,Chars,InStream,R).

incr(X, X1) :- X1 is X 1.

vowel(InStream, R) :- 
    open(InStream, read, In), 
    repeat,
    readWord(In, W),
    close(In).

CodePudding user response:

Here's my attempt (ISO predicates):

% open Src, count vowels, close stream, print to console
count_vowels_in(Src) :-
    open(Src, read, Stream),
    count(Stream, Total),
    close(Stream),
    % cutting here because our Stream is now closed, any backtracking will break things that rely on it
    % you could also put this after at_end_of_stream/1 in count_/3
    % not sure what best practices are here
    !,
    write(Total).

% just a nice wrapper to get started counting with the initial count set to 0
count(Stream, Total) :-
    count_(Stream, 0, Total).

% at end of stream, Count = Total and we're done
count_(Stream, Count, Count) :-
    at_end_of_stream(Stream).

% read from stream recursively, incrementing Count as needed
count_(Stream, Count0, Total) :-
    \ at_end_of_stream(Stream),
    get_char(Stream, Char),
    char_value(Char, Value),
    Count1 is Count0   Value,
    % recursively call count_, but now with our new Count1 value instead, carrying forward the results
    count_(Stream, Count1, Total).

char_value(Char, 1) :-
    vowel(Char).
char_value(Char, 0) :-
    \ vowel(Char).

vowel(a).
vowel(e).
vowel(i).
vowel(o).
vowel(u).

The biggest difference is that I use two variables for keeping track of the count. Count (equivalent to your R) is the current count, and Total is a variable representing the final count. We unify Total with Count when we are finished counting: at the end of the stream.

In the original program posted, there were many singleton variables (variables that are never unified with anything, for example W). This is usually indicative of a bug and will generate warnings. Remember that Prolog is a logical language, it can be good to take a step back and think "what am I actually trying to do with these variables?". It can also help to break the problem down into smaller chunks instead of trying to write one predicate that does everything.

CodePudding user response:

I might approach it like this:

  • Slurp in the entire file as a list of characters.
  • Traverse that list and tally the vowels it contains.
  • Write the tally.

Something like

findall(C, ( get0(V) , V \= -1 , char_code(C,V) ), Cs).

should suffice for slurping the text.

And then, something along these lines:

count_vowels :-
    findall(C, ( get0(V) , V \= -1 , char_code(C,V) ), Cs),
    count_vowels(Cs,N),
    writeln( total_vowels : N )
    .

count_vowels( S  , N ) :- string(S), !, string_chars(S,Cs), count_vowels(Cs,N) .
count_vowels( Cs , N ) :- count_vowels(Cs,0,N) .

count_vowels( []     , N , N ) .
count_vowels( [C|Cs] , T , N ) :- tally(C,T,T1), count_vowels(Cs,T1,N).

tally( C , M , N ) :- vowel(C), !, N is M 1 .
tally( _ , N , N ) .

vowel( a ).
vowel( e ).
vowel( i ).
vowel( o ).
vowel( u ).
  • Related