Поиск в файлах в прологе
Здравствуйте, я пытался создать код, который будет находить данный символ из входного файла и распечатывать его положение, вот что я получил "с помощью похожих проблем, которые я нашел"
process2(Text,POS):-
open('houses.txt', read, In),
get_char(In, Char1),
find(Char1, In,Text,POS),
close(In).
find(Text,In, Text, 0).
find(Char,In,Text,POS) :-
POS is POS1 +1,
get_char(In, Char2),
find(Char2,In,Text,POS1).
однако компилятор выдает эту ошибку: ОШИБКА: is/2: Аргументы не достаточно созданы
4 ответа
При обработке ввода всегда сначала учитывайте DCG:
: - use_module ( библиотека (pio)). process3 (Файл, Текст, POS):- фраза_от_файла (найти (Текст, POS), Файл). найти (Текст, [P|Ps]) -> lazy_list_character_count(P), Текст,!, найти (текст, пс). найти (Текст, Ps) -> [_], найти (Текст, Ps). найти (_Text, []) -> [].
Это находит все позиции входной строки:
?- process3('/home/carlo/.swiplrc', `file`, P).
P = [51, 174, 254, 452, 549, 1977, 2106, 3682, 4033|...] ;
false.
отредактируйте как предложено Борисом, сокращение могло удалить некоторое законное решение. Так что вот бесплатная версия.
find(_Text, []) --> [].
find(Text, [P|Ps]) -->
lazy_list_character_count(P), Text,
find(Text, Ps).
find(Text, Ps) --> \+Text, [_], find(Text, Ps).
Это работает точно так, как ожидается, если вы просто используете ограничения CLP(FD) вместо низкоуровневой арифметики:
:- use_module(library(clpfd)).
find(Text, In, Text, 0).
find(Char, In, Text, POS) :-
POS #= POS1 + 1,
get_char(In, Char2),
find(Char2,In,Text, POS1).
Преимущество версии CLP(FD) заключается в том, что это хвостовая рекурсия, что вы и сделали интуитивно.
Я тоже рекомендую setup_call_cleanup/3
и даже лучше, library(pio)
при обработке файлов. (Примечание: в SICStus Prolog вы можете установить флаг Prolog double_quotes
в chars
, а затем использовать DCG для обработки файла в виде символов! Если вам это интересно, лоббируйте поддержку в SWI!)
Я оставляю это и исправляю оставшиеся (завершение) проблемы в коде в качестве упражнений.
Один очень чистый способ сделать это - использовать DCG, как ответ CapelliC. Как он, используя отличный library(pio)
Ульрих Ноймеркель, например, как в SWI-Prolog, вы можете объединить DCG и phrase_from_file/2
для следующего решения:
:- use_module(library(pio)).
... --> []|[_], ... .
file_pattern_pos(File, Pattern, Pos) :-
phrase_from_file(( ...,
lazy_list_character_count(Pos),
Pattern,
...
),
File).
Это взято дословно из примера кода в документации кphrase_from_file/2
, только что добавлен lazy_list_character_count//1
, В отличие от другого ответа DCG, он генерирует все решения после возврата. Итак, с этим файлом:
$ cat banana.txt
banana
Antananarivo
Вы получаете от верхнего уровня:
?- file_pattern_pos("banana.txt", "ana", Pos).
Pos = 1 ;
Pos = 3 ;
Pos = 10 ;
Pos = 12 ;
false.
Чтобы составить список всех позиций одного символа:
?- bagof(P, file_pattern_pos("banana.txt", "a", P), Ps).
Ps = [1, 3, 5, 10, 12, 14].
Это хорошее решение, потому что к нему легко добраться, просто взглянув на пример кода из документации phrase_from_file/2
, Однако в комментариях ниже были отмечены две проблемы:
- Проблема эффективности;
- С помощью
lazy_list_character_count//1
означает, что вы не можете использовать это сphrase/2
,
Проблема эффективности может быть решена, как указано в комментарии:
... --> [].
... --> [_], ... .
Другая проблема более серьезная. В конце концов, возможно, необходимо подсчитать количество потребляемых символов. Например:
span(N) --> span_(0, N).
span_(N, N) --> [].
span_(N0, N) --> [_],
{ N1 is N0 + 1
},
span_(N1, N).
Теперь мы можем написать с верхнего уровня:
?- phrase_from_file(( span(Pos), "ana", ... ), "banana.txt").
Pos = 1 ;
Pos = 3 ;
Pos = 10 ;
Pos = 12 ;
false.
Или, используя phrase/2
:
?- phrase((span(P), "ana", ...), "banana").
P = 1 ;
P = 3 ;
false.
Если вы намерены использовать SWI-Prolog, вы можете использовать строки для работы с текстом в более простых случаях. В этом случае, например, достаточно прочитать файл из потока In
(как у вас в вашем вопросе) с помощью read_string/3
и найти позиции всех вхождений подстроки в нем, используя sub_string/5
:
setup_call_cleanup(open(File, read, In),
read_string(In, _, File_contents),
close(In)),
sub_string(File_contents, Pos, _Length, _After, Substr)
Вот и все. Pos
будет позиция на основе 0 Substr
, Чтобы найти символы, просто используйте строку длины 1. Одна из лучших вещей о sub_string/5
является то, что он правильно работает с частично перекрывающимися подстроками:
?- sub_string("banana", Pos, _, _, "ana").
Pos = 1 ;
Pos = 3 ;
false.
Более стандартный аналог sub_string/5
является sub_atom/5
с идентичной семантикой как sub_string/5
, но взяв атомы. Он должен быть доступен в каждой реализации Пролога.
?- sub_atom(banana, Pos, _, _, ana).
Pos = 1 ;
Pos = 3 ;
false.
Прочитав весь файл в кодах, просто используйте atom_codes/2
а потом sub_atom/5
, Это, однако, несколько расточительно.
Как только вам придется делать более сложные вещи с содержимым файла, вы можете перейти к использованию DCG, library(pio)
и т. д. Возвращаясь к использованию примитивов чтения как get_char
обычно не требуется. Тем не менее, я все равно настоятельно рекомендую прочитать раздел руководства по строкам, которые я связал выше.