2

I want to open a text file, read (arbitrary) lines from it, and construct program state from the extracted information. In particular, I am wanting to track the line numbers as the file is processed. I've written the code below, but my experience with prolog is limited and I'd appreciate any comments or suggestions that the community has.

I've looked at similar questions on stack overflow and the web, and found the detailed answer from Daniel Lyons (https://stackoverflow.com/a/16096728/11542361) very helpful, but it doesn't track the line numbers. I've also looked at Anne Ogborn's tutorial (Using Definite Clause Grammars in SWI-Prolog; referenced from https://stackoverflow.com/a/14015035/11542361) but the text that I'm looking to process is largely unstructured (with no clear grammar rules).

The code that I've written is:

%% Text processing framework
%
% Idea is to open a file, read lines from it, and construct
% program state. The state below just counts the number of lines
% in the input file, along with the number of lines containing
% the substring "Added" [for illustrative purposes only].

source_file('logfile.txt').

main(_Argv) :-
  source_file(Source_File),
  open(Source_File, read, Input),
  % Sample state: count # lines containing 'Added'
  load_state(Input, state(added(0),lines(0)), State, 0),
  !,               % prohibit backtracking to load_state()
  close(Input),
  print(State).    % process loaded information

%% load_state(+Input, +Old_State, -New_State, +Previous_Line_Number)
load_state(Input, Old_State, New_State, Previous_Line_Number) :-
  Line_Number is Previous_Line_Number + 1,
  read_line_to_string(Input, Line),
  load_state_x(Input, Line, Old_State, New_State, Line_Number).

% load_state_x enforces termination at end_of_file, and also
% invokes recursion back to load_state() for the next line.
load_state_x(_, end_of_file, State, State, _).
load_state_x(Input, Line, Old_State, New_State, Line_Number) :-
  process_line(Line, Old_State, Next_State, Line_Number),
  load_state(Input, Next_State, New_State, Line_Number).

% process_line() has knowledge of the system state, and updates it.
process_line(Line, state(added(Count),lines(_)),
                   state(added(New_Count),lines(Line_Number)), Line_Number) :-
  sub_string(Line,_,_,_,"Added"),
  New_Count is Count + 1.

% "Added" not a substring of Line (per above)
process_line(_Line, state(added(Count),lines(_)),
                    state(added(Count),lines(Line_Number)), Line_Number).

The code above works in SWI-Prolog (in limited testing); it has not been tried on any other prolog systems.

Laurasia
  • 31
  • 4
  • if you're interested only in SWI-Prolog, better to add the relative tag, since it offers several IO facilities not easily ported to other systems. – CapelliC May 23 '19 at 14:35
  • I don't see anything wrong with your code (though as a nitpick I would prefer to use `succ/2` over `N1 is N+1`) but if you are interested in using DCGs, you should consider using [semicontext notation](https://www.metalevel.at/prolog/dcg#semicontext) to keep either your state or the current line number or both. – Daniel Lyons May 23 '19 at 19:04
  • Thank you @DanielLyons - the link you provided is a great reference to advanced DCG usage by Markus Triska. It took me a while to understand how the state was getting implicitly passed around through the DCG rules in the `num_leaves_//` example - the key is the second and third arguments in the call `phrase(num_leaves_(Tree), [0], [N])` which instantiates the state (number of leaves) to 0, and then pulls out the calculated result at the end. It's very clever! – Laurasia May 24 '19 at 00:21
  • @CapelliC - what is the 'relative tag' for SWI-Prolog? I couldn't find any information on it. Is there a link or reference to any information about it? – Laurasia May 24 '19 at 00:29
  • @Laurasia he just means use the [tag:swi-prolog] tag. – Daniel Lyons May 24 '19 at 19:41

0 Answers0