Я хотел бы прочитать обычный текстовый файл и применить предикат к каждой строке (предикаты содержат write
, который выполняет вывод). Как бы я это сделал?
Прочитать файл построчно в Прологе
Ответы (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').
findall/3
для получения всех экземпляров определенного шаблона, но я вижу, что вы просто используете dcg, который анализирует весь файл. Из любопытства, почему мы должны использовать call(eos)
вместо правила DCG?
- person Shon; 24.10.2014
call//1
(а затем eos/2
) используется для переносимой ссылки на все неявные аргументы DCG из правила DCG. Вместо этого вы не можете использовать правило DCG, потому что правила DCG подчиняются правилам перевода, которые позволяют им ссылаться только на определенные части этих аргументов. Переносимость означает, что это не зависит от того, как какая-либо конкретная система Prolog фактически преобразует правила DCG в правила Prolog, поэтому она работает во всех системах, поддерживающих DCG, которые в настоящее время разрабатываются ISO.
- person mat; 24.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
, конечно. Настройте в соответствии с вашим запросом.
Я предполагаю, что этот принцип может быть применен для ответа на ваш вопрос. Удачи.
Есть несколько возможных по количеству и более разумных по производительности решений, чтобы получить неинтерпретированные, т.е. простые текстовые строки из файла:
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, которое считывает строку, а не атом, может быть быстрее. Но в приведенном выше мы сравниваем атом с чтением атома.
Учитывая ответы здесь, я создал это, что больше похоже на 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).