It is too much effort to do this with get_char
and repeat
. At the very least, look at the code sample at the bottom of the docs for repeat
:
repeat,
read(Term),
( Term == end_of_file
-> !
; process(Term),
fail
).
You’d also need to use a mutable variable for the counting. You can see examples of mutable variables in the implementation of aggregate_all
.
Putting those together I get:
line_count(Filename, N) :-
setup_call_cleanup(open(Filename, read, In),
stream_line_count(In, N),
close(In)).
stream_line_count(In, N) :-
Count = count(0),
repeat,
get_char(In, X),
( X == end_of_file
-> !
; ( X == '\n'
-> arg(1, Count, N0),
N1 is N0 + 1,
nb_setarg(1, Count, N1)
),
fail
),
arg(1, Count, N).
and then:
?- line_count("line_count.pl", N).
N = 19.
One easier way is using read_string/5
and using a loop instead of repeat
:
line_count(Filename, N) :-
setup_call_cleanup(open(Filename, read, In),
stream_line_count(In, 0, N), % note the extra argument!
close(In)).
stream_line_count(In, N0, N) :-
read_string(In, "\n", "", Sep, _),
Sep \== -1,
!,
N1 is N0 + 1,
stream_line_count(In, N1, N).
stream_line_count(_, N, N).
Instead of read_string/5
you could have used one of the predicates in library(readutil). Or maybe you can use a DCG:
:- use_module(library(dcg/basics)).
line_count(N0, N) -->
string_without("\n", _), "\n",
!,
{ N1 is N0 + 1 },
line_count(N1, N).
line_count(N, N) --> [].
and then:
?- phrase_from_file(line_count(0, N), "line_count.pl").
N = 8.
There are endless possibilities (do you have to do it in constant memory? or can you just read the whole file with read_string/3
and count the newlines in this? and so on).