Прочитать файл построчно в Прологе

Я хотел бы прочитать обычный текстовый файл и применить предикат к каждой строке (предикаты содержат write, который выполняет вывод). Как бы я это сделал?


person Igor Marvinsky    schedule 26.01.2011    source источник


Ответы (4)


В SWI-Prolog самое чистое решение — написать DCG, описывающую, что такое «линия», а затем вызвать предикат для каждой строки. Используйте библиотеку (pio), чтобы применить DCG к файлу.

EDIT: в соответствии с запросом учтите следующее:

:- use_module(library(pio)).

lines([])           --> call(eos), !.
lines([Line|Lines]) --> line(Line), lines(Lines).

eos([], []).

line([])     --> ( "\n" ; call(eos) ), !.
line([L|Ls]) --> [L], line(Ls).

Пример использования: ?- phrase_from_file(lines(Ls), 'your_file.txt').

person mat    schedule 26.01.2011
comment
Я знаю, что прошло много времени, но я пробую этот метод, и мне кажется, что это занимает абсурдно много времени. Не могли бы вы привести пример некоторого производительного кода с использованием DCG и библиотеки (pio), который будет читать файл построчно? Спасибо! - person Shon; 22.10.2014
comment
Большое спасибо! Я вижу свою ошибку до того, как использовал пример в документации библиотеки SWI-Prolog (pio) для моей модели. Он использует findall/3 для получения всех экземпляров определенного шаблона, но я вижу, что вы просто используете dcg, который анализирует весь файл. Из любопытства, почему мы должны использовать call(eos) вместо правила DCG? - person Shon; 24.10.2014
comment
call//1 (а затем eos/2) используется для переносимой ссылки на все неявные аргументы DCG из правила DCG. Вместо этого вы не можете использовать правило DCG, потому что правила DCG подчиняются правилам перевода, которые позволяют им ссылаться только на определенные части этих аргументов. Переносимость означает, что это не зависит от того, как какая-либо конкретная система Prolog фактически преобразует правила DCG в правила Prolog, поэтому она работает во всех системах, поддерживающих DCG, которые в настоящее время разрабатываются ISO. - person mat; 24.10.2014
comment
Спасибо еще раз! Я переработал проблему, которую пытался решить, с помощью вашего примера, и в итоге получилось довольно элегантное решение. Что еще более важно, вы помогли мне поднять мое понимание DCG на новый уровень. (Я изучал их случайным образом, время от времени, больше года, и я все еще чувствую, что у меня ограниченное понимание. Это такая простая концепция, и все же... может быть, это не так просто?). - person Shon; 26.10.2014

Вы можете использовать read для чтения потока. Не забудьте вызвать at_end_of_stream, чтобы исключить синтаксические ошибки.

Пример:

readFile.pl

main :-
    open('myFile.txt', read, Str),
    read_file(Str,Lines),
    close(Str),
    write(Lines), nl.

read_file(Stream,[]) :-
    at_end_of_stream(Stream).

read_file(Stream,[X|L]) :-
    \+ at_end_of_stream(Stream),
    read(Stream,X),
    read_file(Stream,L).

мой файл.txt

'line 0'.
'line 1'.
'line 2'.
'line 3'.
'line 4'.
'line 5'.
'line 6'.
'line 7'.
'line 8'.
'line 9'.

Таким образом, вызвав main, вы получите результат:

?- main.
[line 0,line 1,line 2,line 3,line 4,line 5,line 6,line 7,line 8,line 9]
true 

Просто настройте main. Вывод здесь является примером использования write, конечно. Настройте в соответствии с вашим запросом.

Я предполагаю, что этот принцип может быть применен для ответа на ваш вопрос. Удачи.

person Ishq    schedule 26.01.2011
comment
Некоторое время спустя, но я получаю сообщение об ошибке с этим кодом: «неперехваченное исключение: ошибка (existence_error (процедура, main/0), top_level/0)». Знаете, что это значит? - person RK_97; 29.11.2020

Есть несколько возможных по количеству и более разумных по производительности решений, чтобы получить неинтерпретированные, т.е. простые текстовые строки из файла:

SWI-Пролог:

read_line(S, X) :- 
   read_line_to_codes(S, L), 
   read_line2(L, X).

read_line2(end_of_file, _) :- !, fail.
read_line2(L, X) :-
   atom_codes(X, L).

Джекекек Пролог:

:- use_module(library(stream/console)).

Вот некоторые тайминги, чтение файла из 655 строк:

test :-
   open('<path>', read, Stream),
   test(Stream),
   close(Stream).

test(Stream) :-
   read_line(Stream, _), !,
   test(Stream).
test(_).

SWI-Пролог:

̀?- time((between(1,100,_), test, fail; true)).
% 328,300 inferences, 0.125 CPU in 0.143 seconds (88% CPU, 2626400 Lips)
true.

Джекекек Пролог:

?- time((between(1,100,_), test, fail; true)).
% Up 121 ms, GC 2 ms, Thread Cpu 94 ms (Current 05/07/19 17:19:05)
Yes

Я предполагаю, что решение SWI-Prolog, которое считывает строку, а не атом, может быть быстрее. Но в приведенном выше мы сравниваем атом с чтением атома.

person Mostowski Collapse    schedule 07.05.2019

Учитывая ответы здесь, я создал это, что больше похоже на python с:

?- read_file('test.txt', tokenize,5,L). %first 5 lines
?- read_file('test.txt', tokenize,L). %the whole file
?- read_file('test.txt', split,5,L). %just split
?- open('test.txt',read,S), read_lines(S,split,5,L), close(S).

код :

:- module(files,[read_line/3, read_file/3,  read_file/4, read_lines/3, read_lines/4, split/2, split/3, split/4]).

:- use_module(library(pcre)).

string2atoms(Strings, Atoms) :- maplist(atom_string, Atoms, Strings).
split(Str, Lst) :- split_string(Str, " ", "", Lst).
split(Str, Separator, Lst) :- split_string(Str, Separator, "", Lst).
split(Str, Separator, Pad, Lst) :- split_string(Str, Separator, Pad, Lst).
is_empty(Str) :- re_match(Str, '^\s*$').
non_empty(Str) :- ( is_empty(Str) -> false ; true).

tokenize(String,Tokens) :- split(String,Lst), string2atoms(Lst,Tokens).

%read a line and execute a Goal on it
read_line(Stream,Goal,Args) :- 
    \+ at_end_of_stream(Stream), read_line_to_string(Stream,Str),
    %% \+ isempty(Str), call(Goal,Str,Args). 
    ( is_empty(Str) -> true ; call(Goal,Str,Args)). 

% given Stream execute Goal on every line. with the option to process only N lines
read_lines(Stream, _, _,_) :- at_end_of_stream(Stream), !. %is EOF
read_lines(_, _, 0,_) :- !. % only N lines
read_lines(Stream, Goal, N, [Res|T]) :-
    N0 is N - 1, read_line(Stream, Goal, Res), writeln(Res),
    read_lines(Stream, Goal, N0, T).

%read the whole file
read_lines(Stream, Goal, LoL) :- read_lines(Stream, Goal, 1000000, LoL).

%given file name execute Goal on every line
read_file(File, Goal, N, Res) :-
    open(File, read, Stream), read_lines(Stream, Goal, N, Res), close(Stream).
read_file(File, Goal, Res) :- read_file(File, Goal, 1000000, Res).
person sten    schedule 03.01.2021