Поиск в файлах в прологе

Здравствуйте, я пытался создать код, который будет находить данный символ из входного файла и распечатывать его положение, вот что я получил "с помощью похожих проблем, которые я нашел"

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, Однако в комментариях ниже были отмечены две проблемы:

  1. Проблема эффективности;
  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 обычно не требуется. Тем не менее, я все равно настоятельно рекомендую прочитать раздел руководства по строкам, которые я связал выше.

Другие вопросы по тегам