Модули в SWI Prolog. Подключение файлов

      Комментарии к записи Модули в SWI Prolog. Подключение файлов отключены

В этой теме 1 ответ, 2 участника, последнее обновление  Васильев Владимир Сергеевич 2 года/лет, 2 мес. назад.

  • Автор
    Сообщения
  • #2161

    questioner
    Участник

    Я хочу разместить исходный код проекта в нескольких файлах, каким образом я могу их соединить вместе?
    Мне нужен аналог директивы #include или import.
    Язык Prolog является интерпретируемым, поэтому, я полагаю, в нем должны быть средства подключения файлов во время выполнения программы.

  • #2261

    Система модулей в SWI Prolog соответствует стандарту ISO, поэтому используются такие же предикаты как и в SICStus Prolog, YAP и т.п.

    Модулем в Prolog является файл с исходным кодом, который можно подключить точно также, как базу данных, сохраненную в файле. Однако, от БД модуль отличается тем, что его содержимое разделяется на две части — открытую (интерфейс, доступный клиентам) и закрытую (детали реализации). При подключении файла при помощи consult, все его содержимое (включая реализацию) будет добавлено во внутреннюю базу данных и станет доступно для использования:

    :- [module_name].
    :- consult(module_name).

    Приведенные записи являются эквивалентными, обе они включают файлы целиком, не относясь к ним как к полноценным модулям. При использовании директивы use_module содержимое файлов в БД не копируется, доступными для вызова остаются лишь экспортированные функции
    :- use_module(module_path).

    Если модули расположены в стандартном каталоге среды Prolog, то используется вспомогательная директива library(module_file_name), которая добавит необходимый путь до файла. В противном случае задается относительный или абсолютный путь, например:

    • C:/strings_module модуль расположен на диске C (не рекомендуется задавать пути таким образом для сохранения переносимости кода);
    • loop/tail_call_elimination для подключения модуля, расположенного во вложенном каталоге с именем loop;
    • ../utils/strings_module — в родительском каталоге есть вложенный каталог utils, в котором расположен необходимый модуль.

    В начале каждого модуля должно находиться описание, содержащее имя модуля (обычно совпадает с именем файла без расширения) и списка экспортируемых функций (составляющих интерфейс модуля). Например, модуль мог бы предоставлять функцию разделения строки на список слов:
    :- module(string_to_words_module, [string_to_words/2]).
    При перечислении экспортируемых модулем функций через слэш указывается указывается количество аргументов функции. Внутри модуля обязательно должна быть реализация экспортируемой функции, но могут быть и другие, вспомогательные (клиенты не смогут к ним обратиться).

    Директива use_module в Prolog импортирует имена функций модуля, т.е. делает их доступными для использования. При этом очень часто может возникать конфликт имен. Например, несколько модулей могут предоставлять функцию с одним и тем же именем.

    Допустим два модуля предоставляют функции с одинаковым именем. Есть два варианта:

    • функции выполняют одну и туже работу (код дублируется, но по какой-либо причине мы не можем устранить это — например, модули разработаны не нами, а другими, независящими друг от друга разработчиками).

      Например, разные программисты могли бы реализовать различные модули работы со строками, возможно нам понадобились какие-либо функции из разных таких модулей, однако с большой вероятностью частично модули будут дублировать функциональность друг друга. Допустим, оба модуля предоставляют функцию string_to_list/2 для преобразования строки в список символов;

    • Функции имеют разное назначение, одну или обе из них надо переименовать, но изменить чужие модули мы не можем. Например, два модуля реализуют разные методы оптимизации — удаление мертвого кода (dead code elimination) и оптимизацию хвостового вызова (tail call elimination), оба модуля предоставляют функцию run, запускающую оптимизацию. Было бы здорово переименовать эти функции при импорте в tail_call_elimination и dead_code_elimination соответственно, чтобы внутри нашего модуля не было конфликта имен, а сами имена наиболее полно отражали суть.

    Обе проблемы решаются дополнительным аргументом директивы use_module, позволяющей исключить какие-либо экспортируемые модулем функции или переименовать их:

    :- use_module(library(strings_module_1), [divide_list/2, string_to_list/2]).
    :- use_module(library(strings_module_2), except([string_to_list/2])).
    :- use_module(library(dead_code_elimination_module), [run/0 as dead_code_elimination/0]).
    :- use_module(library(tail_call_elimination_module), [run/0 as tail_call_elimination/0]).
    

    В приведенном примере подключаются 4 модуля:
    1. импортирует 2 функции, все остальное, указанное в списке экспорта модуля исключается;
    2. импортирует все кроме одной функции (устраняя конфликт имен);
    3-4. импортируют две функции используя переименования в связи с конфликтом имен.

    Модули в Prolog могут иерархически вкладываться друг в друга, при этом модуль может экспортировать не только свои собственные функции, но и функции вложенных модулей. Для этого применяется директива reexport, добавить в список экспорта текущего модуля их нельзя.

    Так, например, модуль работы со словами строки мог бы использовать модуль работы с отдельными символами и помимо собственных функций выполнять реэкспорт функции преобразования строки в список символов и функций:

    :- module(words_processing_module, [string_to_words/2]).
    :- reexport(strings_character_processing, [string_to_list/2]).
    

    Директив reexport может быть задано несколько. В заданном примере выполняется реэкспорт лишь одной функции, однако reexport/1 принимает список модулей и выполняет реэкспорт всех их интерфейсов. Кроме того, также как в use_module может использоваться опция except, задающая список функций, исключенных для реэкспорта.

    В SWI Prolog возможно описания пользовательских операторов, а также их экспорт из модуля.

    Так, например, в своем проекте, связанном с синтаксическим анализом я очень часто использую предикат разбиения строки на части. Для сокращения записи я описал оператор:

    op(200, xfy, <-). % 200 - приоритет операции, xfy означает что оператор бинарный
    String <- (Parts):-
    divide_list(String, Parts).

    Для экспорта оператора описание (директивой op/3) переносится в список экспорта модуля перед экспортом оператора:

    :- module(strings, [
    op(200, xfy, <-), % описание оператора
    (<-)/2, % экспорт оператора
    rd_line/1 % экспорт других функций
    ]).
    

Для ответа в этой теме необходимо авторизоваться.