Основы Системы Visual Prolog: различия между версиями

Материал из wikiru.visual-prolog.com

(Новая: {{FundamentalVisualPrologNavbar}} In this tutorial, we will introduce a program that is developed on the Visual Prolog platform. The essential algorithms of the tutorial are the same as...)
 
 
(не показано 66 промежуточных версий 2 участников)
Строка 1: Строка 1:
{{FundamentalVisualPrologNavbar}}
{{FundamentalVisualPrologNavbar}}


In this tutorial, we will introduce a program that is developed on the Visual Prolog platform. The essential algorithms of the tutorial are the same as those that were developed in the Part 2 of the "Fundamental Prolog" Tutorial.
В этом руководстве мы представляем программу, разработанную на платформе системы программирования Visual Prolog. Алгоритмы, используемые в руководстве , - те же, что и в руководстве "Fundamental Prolog" (Часть 2).


==Differences in Visual Prolog and Traditional Prolog==
==Отличия между Visual Prolog и традиционным Прологом==


The differences between traditional Prolog and Visual Prolog can be broadly divided into these categories:
Различия между традиционным Прологом и Visual Prolog можно провести по следующим категориям:


*Program structure differences: There are distinct, yet easy to understand differences between the structure used in traditional Prolog and that used in Visual Prolog. It essentially comprises of understanding how to mark out the declarations from the definitions, and to indicate the main ''Goal'' that the program has to seek using specific keywords.
*'''Различия в структуре программы''':<br/>Различия между Visual Prolog и традиционным Прологом имеются, но они не существенны. Они сводятся к пониманию того, как различаются декларации (declarations) от определений (definitions), а также к явному выделению цели ''Goal''. Все это поддержано специальными ключевыми словами.


*File considerations: Visual Prolog gives various facilities to organize the structure of the program into different kinds of files.
*'''Файловая структура программы:''':<br/>Visual Prolog предоставляет возможности структуризации программ с использованием файлов различного типа.


*Scope access issues: A Visual Prolog program can pick up functionality developed in other modules using a concept called ''scope identification''.
*'''Границы видимости:'''<br/>Программа системы Visual Prolog может поддерживать функционирование, располагающееся в различных модулях с использованием концепции ''идентификация пространств''.


*Object orientation: A Visual Prolog program can be written as an object-oriented program, using classic object oriented features.
*'''Объектная ориентированность:'''<br/>Программа на языке Visual Prolog может быть написана как объектно ориентированная программа с использованием классических свойств объектно-ориентированной парадигмы.


===Program Structure Differences===
===Различия в структуре программ===


====Declarations and Definitions====
====Декларации и Определения====


In Prolog, when we need to use a predicate we simply do so without any prior intimation to the Prolog engine about our intention. For example; in the earlier tutorial the grandFather predicate's clause was directly written down using the traditional Prolog's predicate head and body construction. We did not bother to inform the engine explicitly in the code that such a predicate construction is to be expected later.
В Прологе, когда необходимо использовать предикат, то это делается без каких-либо предварительных указаний движку Пролога о таких намерениях. Например, в предыдущих руководствах клауз предиката grandFather (дедушка) был непосредственно записан с использованием традиционной для Пролога конструкции голова-тело. Мы не беспокоились о том, чтобы в тексте программы предупредить движок явно, что такая конструкция придиката потребуется.


Similarly, when a compound domain is to be used in traditional Prolog, we can use it without first forewarning the Prolog engine about our intention to use the domain. We simply use a domain, the moment we feel the need for it.
Аналогично, когда в традиционном Прологе требуется использовать составной домен, мы можем его использовать без предупреждения движка по поводу этого намерения. Мы просто используем домен тогда, когда в этом возникает необходимость.


However, in Visual Prolog, before writing down the code for the clause body of a predicate, we would first need to declare the existence of such a predicate to the compiler. Similarly, before using any domains, they need to be declared and their presence is to be informed to the compiler.
Однако, в Visual Prolog, перед написанием кода для тела клауза предиката мы должны сначала объявить о существовании такого предиката компилятору. Аналогично, перед использованием любых доменов они должны быть объявлены и представлены компилятору.


The reason such forewarnings are required in Visual Prolog is essentially to ensure that runtime exceptions are converted into compile time errors as far as possible.
Причиной такой необходимости в предупреждениях является попытка как можно раньше обнаружить возможность исключений периода исполнения.


By ''"runtime exceptions"'', we mean the issues that turn up only at the time of running the program that were compiled with it. For example, if you had intended to use an integer as the argument of a functor, and instead of the integer you had erroneously used a real number, it would become a ''runtime'' error (in programs written using most other compilers, but not Visual Prolog) and the program would fail there.
Под ''"исключениями периода исполнения (runtime exceptions)"'', мы понимаем события, возникающие только во время исполнения программы. Например, если Вы вознамерились использовать целое число в качестве аргумента функтора, а вместо этого вы по ошибке использовали вещественное число, то в процессе исполнения возникла бы ошибка ''периода исполнения'' (в программах, написанных для ряда компиляторов, не не для Visual Prolog) и программа в этом случае завершилась бы неуспешно.


When you also declare the predicates and domains that are defined, such kind of positional grammar (which argument belongs to which domain), etc. is made available to the compiler. Therefore, when Visual Prolog performs a compilation, it checks the program quite thoroughly to weed out such grammatical and other mistakes.
Когда Вы объявляете предикаты или доменты, которые определены, то появляется своего рода позиционная грамматика (какому домену принадлежит какой аргумент), доступная компилятору. Более того, когда Visual Prolog выполняет компиляцию, он тщательно проверяет программу на наличие таких грамматических ошибок, наряду с другими.


Because of this feature in Visual Prolog, the overall efficiency of a programmer improves. The programmer does not have to wait till the program is actually executed to detect a bug. In fact, those of you, who are experienced in programming, would understand what a life-saver this can be; for often, the particular sequence of events that is causing a ''runtime exception'' to happen at run-time may be so elusive that the bug may actually turn up after several years, or it may manifest itself in some crucially critical or other embarrassing situations!
Благодяря этому свойству Visual Prolog, повышается конечная эффективность программиста. Программист не должен ждать, когда реально работаящая программа совершит ошибку. Действительно, те из Вас, кто имеет опыт в программировании, понимает насколько этот код безопасен. Часто конкретная последовательность событий, приводящая к исключению периода исполнения кажется настолько неуловимой, что ошибка может проявиться через много лет, или она может заявить о себе в критической ситуации и привести к непредсказуемым последствиям!


All this automatically implies that the compiler '''has''' to be given explicit instructions regarding the predicates and domains that exist in the code using appropriate declarations before the same are defined.
Все это автоматически ведет к тому, что компилятор '''должен''' получать точные инструкции по поводу предикатов и доменов в виде соответствующих объявлений, которые должны предшествовать определениям.


====Keywords====
====Ключевые слова====


A Visual Prolog program consists of Prolog code which is punctuated into different '''sections''' by appropriate keywords that inform the compiler the code it has to generate. For example, there are keywords that differentiate the ''declarations'' from the ''definitions'' of predicates and domains. Usually, each section is preceded by a keyword. There is normally no keyword which signifies the ending of a particular section. The presence of another keyword indicates the ending of the previous section, and the starting of the next one.
Программа на Visual Prolog, представляемая кодом, разделяется ключевыми словами на '''секции''' разного вида путем использования ключевых слов, предписывающих компилятору, какой код генерировать. К примеру, есть ключевые слова, обозначающие различие между '''декларациями''' и '''определениями''' предикатов и доменов. Обычно, каждой секции предшествует ключевое слово. Ключевых слов, обозначающих окончание '''секции''', нет. Наличие другого ключевого слова обозначает окончание предыдущей '''секции''' и начало другой.


The exception to this rule, are the keywords "implement" and "end implement" The code contained between these two keywords indicates the code to be used for a particular class. Those of you who do not understand the concept of a "class" can, for now (i.e. in this tutorial), think of it as a module or a section of the overall program code.
Исключением из этого правила являются ключевые слова '''implement''' и '''end implement'''. Код, содержащийся между этими ключевыми словами, есть код, который относится к конкретному классу. Те, кто не понимает концепцию '''класса''', может пока (в рамках этого руководства) представлять себе его как модуль или раздел какой-то программы более высокого уровня.


For the purpose of this tutorial, we'll introduce only the following keywords (given below). We are also giving the purpose behind these keywords, and the actual syntax can be easily learnt from the documentation. There are other keywords also in Visual Prolog, and those can easily be picked up in later tutorials and the documentation.
В рамках этого руководства мы представляем только часть ключевых слов, приведенных ниже. Мы также объясняем назначение этих ключевых слов, а конкретный синтаксис легко может быть изучен по документации. В Visual Prolog есть и другие ключевые слова, они упоминаются в других рукодствах.


The list of the keywords that you need to know in this tutorial is the following:
В этом руководстве используются следующие ключевые слова:


implement and end implement
'''implement''' и '''end implement'''


Among all the keywords discussed here, this is the only one, which exists as a pair. Visual Prolog treats the code written between these two keywords as the code that belongs to one class. The name of the class MUST be given after the implement keyword.
:Среди всех ключевых слов, обсуждаемых здесь, это единственные ключевые слова, используемые парно. Visual Prolog рассматривает код, помещенный между этими ключевыми словами, как код, принадлежащий одному классу. За ключевым словом '''implement''' обязательно ДОЛЖНО следовать имя класса.


open
'''open'''


This keyword is used to extend the ''scope visibility'' of the class. It is to be used just after the implement keyword.
:Это ключевое слово используется для расширения '''области видимости''' класса. Оно должно быть помещено вначале кода класса, сразу после ключевого слова '''implement''' (с именем класса и, возможно именем интерфейса - прим. перев.).


constants
'''constants'''
 
This keyword is used to mark a section of the code that defines some commonly used values in the program code. For example, if the string literal "PDC Prolog" is to be used in multiple locations throughout the code, then you can define a mnemonic (a short-form, easily remembered word) for the same thus:


:Это ключевое слово исползуется для обозначения секции кода, котора определяет неоднократно используемые значения, применяемые в коде. Например, если строковый литерал "PDC Prolog" предполагается использовать в различных местах кода, тогда можно единожды определить мнемоническое (краткое, легко запоминаемое слово) для использовани в таких местах:
<blockquote>
<vip>constants
<vip>constants
pdc="PDC Prolog".</vip>
pdc="PDC Prolog".</vip>
</blockquote>
:Заметьте, что определение константы завершается точкой (.). В отличие от переменных Пролога константы должны начинаться со строчной буквы (нижний регистр).


Note that the definition of a constant ends in a period (.). Unlike a Prolog variable, a constant should be a word starting with a lower case letter.
'''domains'''


domains
:Это ключевое слово используется для обозначения секции, объявляющей домены, которые будут использованы в коде. Синтаксис таких объявлений позволяет порождать множество вариантов  объявлений доменов, используемых в тексте программы. В пределах этого руководства начального уровня мы не будем вдаваться в детали объаявлений доменов.


This keyword is used to mark a section declaring the domains that would be used in the code. There are many variations for the syntax of such domain declarations, and they cater to the all the possible kinds of domains that would be used later on in the code. As this tutorial is a basic one, we shall not get into the finer details of the domain declarations that can be possible.
:Вообще говоря, объявляется функтор, который будет использоваться в качестве домена и ряд доменов, которые используются в качестве его аргументов. Функторы и составные домены рассматриваются в соответствующем руководстве.


To summarize here, you would be declaring the functor that would be used for the domain and the kind of domains that would form its arguments. Functors and compound domains were explained in detail in the previous part of the Tutorial.
'''class facts'''


class facts
:Это ключевое слово представляет секцию, в которой объявляются факты, которые будут в дальнейшем использоваться в тексте программы. Каждый факт объявляется как имя, используемое для обозначения факта, и набор аргументов, каждый из которых должен соответствовать либо стандартному (предопределенному), либо объявленному домену.


This keyword designates a section, which declares the facts that would be used later on in the code of the program. Each fact is declared with the name used to signify the fact and the arguments that are used for the respective facts along with the domains that those arguments belong to.
'''class predicates'''


class predicates
:Эта секция содержит объявления предикатов, которые определяются в тексте программы в разделе clauses. И опять, объявление предиката - это имя, которое присваивается предикату, и набор аргументов, каждый из которых должен соответствовать либо стандартному (предопределенному), либо объявленному домену.


This section contains the declarations of the predicates that would be later defined in the clauses section of the code. Once again, the names that would be used for these predicates along with the arguments and the domains, to which the arguments belong to, would be indicated in this section.
'''clauses'''


clauses
:Среди всех разделов, существующих в тексте программ на Visual Prolog, это единственный раздел, который близко совпадает с традиционными программами на Прологе. Он содержит конкретные определения объявленных в разделе '''class predicates''' предикатов, причем синтаксически им соответствующие.


Of all the sections that are present in a Visual Prolog code, this section is the one that closely mimics a traditional Prolog program. It contains the actual ''definitions'' of the previously declared predicates. And you would find that the predicates used here would follow the syntax as declared in the ''class predicates'' section.
'''goal'''


goal
:Этот раздел определяет главную точку входа в программу на языке системы Visual Prolog. Более детальное описание приводится ниже.


This section defines the main entry point into a Visual Prolog program. A more detailed explanation is given below.
====Goal (цель)====


====Goal====
В традиционном Прологе, как только какой-либо предикат определен в тексте, Пролог-машине может быть предписано выполнение программы, начиная с этого предиката. В Visual Prolog это не так. Будучи ''компилятором'', он отвечает за генерацию эффективного исполняемого кода написанной программы. Этот код исполняется не в то же время, когда ''компилятор'' порождает код. Поэтому ''компилятору'' надо знать заранее точно, с какого предиката начнется исполнение программы. Благодаря этому, когда программа вызывается на исполнение, она начинает работу с правильной начальной точки. Как можно уже догадаться, откомпилированная программа может работать независимо от компилятора Visual Prolog и без участия среды IDE.


In traditional Prolog, whenever a predicate is defined in the code, the Prolog Engine can be instructed to start the code execution from that predicate onwards. However, that is not the case with Visual Prolog. Being a ''compiler'' it has the responsibility of producing efficiently executing code for the program you write. This code would not be actually executing at the same time the ''compiler'' is doing its work. Hence, the ''compiler'' needs to know beforehand the exact predicate from which the code execution would start, so that later on when the program is called to perform, it can do so from the correct starting point. As you may have guessed, the compiled program can run independently without the Visual Prolog compiler or the IDE itself.
Для того, чтобы это стало возможным, введен специальный раздел, обозначенный ключевым словом ''Goal''. Его можно представлять как особый предикат, не имеющий аргументов. Это предикат - именно тот предикат, с которого вся программа начинает исполняться.


In order to do that, there is a special section indicated by the keyword ''Goal''. Think of it as a special predicate that you would write without arguments. That predicate is the one from which the entire program execution will start.
===Файловое структурирование программ===


===File Considerations===
Необходимость помещения всех частей большой программы в один файл несомненно является неудобством. Это ведет к тому, что программа становится нечитаемой и иногда неправильной. Visual Prolog предусматривает возможность деления текста программы на отдельные файлы, используя среду IDE (Integrated Development Environment) и, кроме того, IDE позволяет помещать логически связанные фрагменты текста программы в отдельные файлы. Несмотря на такое разделение программы на части, сущности, находящиеся в общем использовании, доступны.


Many times, it may become cumbersome to put all the parts of the program into one file itself. It may even make the program unreadable and sometimes incorrect. Visual Prolog has the capability of dividing the program code into separate files using the IDE (Integrated Development Environment) and it is possible to write neat pieces of code into separate files using that IDE. When it is done in that manner, things that are to be used commonly can be accessed across files. E.g. If you have a domain that is to be used in multiple files, then the declaration of that domain is done in a separate file, and that file is then accessed from other files.
Например, если имеется домен, который используется несколькими файлами, то объявление домена делается в отдельном файле и к этому файлу из других файлов существует доступ.


However, for the purpose of simplifying this particular tutorial, we shall predominantly be using only one file to develop the program code. In the course of constructing the program, the IDE would automatically create some more files which we can ignore for the time being. We can learn about them in future tutorials.
Для упрощения этого руководства мы будем использовать один файл для разработки текста программы. На самом деле в процессе построения программы среда IDE создавала бы автоматически несколько файлов, но мы сейчас будем это умалчивать. Об этом написано в других руководствах.


===Scope Access Issues===
===Расширение Области Видимости===


Visual Prolog divides the total program code into separate parts, each part defining one class. In object oriented languages, a class is a package of code and its associated data which is put together. This requires more explanation which would be available in later tutorials. As noted earlier, for those of you who are not familiar with object oriented programs, you can think of a ''class'' loosely as being synonymous to ''modules''. Normally, Visual Prolog defines each class in its own separate file.
В Visual Prolog текст программы разделен на отдельные части, какждая часть определяется как класс (class). В объектно-ориентированных языках программирования, класс - это пакет кода и ассоциированные с ним данные. Это требует длительных объяснений и сделано в других руководствах. Те, кто не знаком с объектно-ориентированным программированием, может представлять себе '''класс''' нестрого как синоним понятия '''модуль'''. В Visual Prolog каждый класс обычно помещается в отдельный файл.


During the program execution, it often so happens that the program may need to invoke a predicate that is actually defined in another class (file). Similarly, data (constants) or domains defined in a class may need to be accessed from a different file.
В процессе исполнения программы, часто бывает так, что программе может потребоваться вызвать предикат, который определен в другом классе (файле). Аналогично, данные (константы) или домены могут быть востребованы в другом файле.


Visual Prolog allows such cross class code/data references using a concept called scope access. This can be understood by an example. Suppose there is a predicate called pred1 defined in a class called class1 (which is written down in another file using the IDE), and we need to call that predicate in the clause body of some other predicate, pred2 in a different file (say class2) then this is how pred1 would be called within the clause body of pred2 :
Visual Prolog позволяет делать такие взаимные ссылки на предикаты или данные используя так называемую концепцию области видимости (scope access). Это может стать понятным на примере. Предположим имеется предикат, называемый ''pred1'' и определенный в классе называемом ''class1'' (который помещается в другом файле, согласно стратегии среды IDE), и мы хотим вызвать этот предикат в теле клауза некоторого другого предиката ''pred2'', находящегося в другом файле (скажем, ''class2''). Тогда вот как предикат ''pred1'' должен бы быть вызван в теле клауза предиката ''pred2'' :


<vip>clauses
<vip>clauses
    pred3:-
  pred3:-
        ...
    ...
        !.
    !.


    pred2:-
  pred2:-
        class1::pred1, %pred1 is not known in this file. It is defined in some other file, hence a class qualifier is needed
    class1::pred1, % pred1 неизвестен в этом файле.  
        pred3,
                  % Он определен в другом файле,  
        ...</vip>
                  % поэтому требуется квалификатор класса.
    pred3,
    ...
</vip>


In the above example, you can see that the clause body of pred2 calls two predicates pred1 and pred3. As pred1 is defined in another file (which defines class1), the word class1 with the token '''::''' (two colons) precedes the word pred1. This can be called as a class qualifier.But the predicate pred3 is defined within the same file as pred2 itself. Hence there is no need to indicate class2:: prior to the predicate call for pred3.
В приведенном примере видно, что тело клауза предиката ''pred2'' вызывает два предиката ''pred1'' и ''pred3''. Поскольку ''pred1'' определен в другом файле (где определен класс ''class1''), постольку слово ''class1'' с символом '''::''' (два двоеточия) предшествует слову ''pred1''. Это можно назвать как квалификатор класса. Но предикат ''pred3'' определен внутри того же самого файла, что и предикат ''pred2''. Поэтому здесь нет необходимости использовать ''class2::'' перед вызовом  предиката ''pred3''.


This behavior is technically explained thus: The ''scope visibility'' of pred3 is within the same scope of pred2. Hence there is no need to clarify that pred3 is from the same class as pred2. The compiler would automatically look for the definition of pred3 within the same scope area as defined for class2.
Технически такое поведение объясняется следующим: ''Область видимости (scope visibility)'' предиката ''pred3'' находится внутри той же области, где находится предикат ''pred2''. Поэтому здесь не нужно показывать, что ''pred3'' и ''pred2'' находятся в одном классе. Компилятор автоматически увидит определение предиката ''pred3'' внутри той же области, в классе ''class2''.


The scope area of a particular class definition is restricted to the class that is implemented in a particular file (i.e. code that is written within the implement ''-'' end implement keywords). The predicates defined therein can call each other without the class qualifier and the double colon (::) token preceding it.
Область видимости конкретного класса лежит внутри границ, где класс определен (то есть в интервале между ключевыми словами '''implement''' - '''end implement''' ). Предикаты, определенные здесь, могут вызывать друг друга без квалификатора класса и двойного двоеточия (::).


The scope area of a class can be extended by using the open keyword. This keyword informs the compiler to bring in names (of predicates / constants / domains) that were defined in other files. If a scope area is extended, then we need not write the class qualifier with the double colon.
Область видимости класса может быть расширена использованием ключевого слова '''open'''. Это ключевое слово информирует компилятор о том, что в этот класс должны быть "доставлены" имена (предикатов / констант / доменов), которые были определены в других файлах. Если область видимости расширена, то необходимости использования квалификатора класса (с двойным двоеточием) нет.


<vip>open class1
<vip>
            ...
open class1
  ...


clauses
clauses
    pred3:-
  pred3:-
        ...
    ...
        !.
    !.


    pred2:-
  pred2:-
        pred1, %Note: "class1::" qualifier is not needed anymore, as the scope area is extended using the 'open' keyword
    pred1, % Внимание: квалификатор "class1::" больше не нужен,
        pred3,
          % поскольку область видимости расширена
        ...</vip>
          % использованием ключевого слова 'open'
    pred3,
    ...</vip>


===Object Orientation===
===Объектная ориентированность===


The current version of Visual Prolog is a strongly object oriented language. The ENTIRE code which is developed for a program is put into appropriate classes, as needed. This happens by default, even if you are not interested in the object oriented features of the language. You would notice this feature in the example given in this tutorial also. The code is inserted into a class called "family1" even though we would eventually not use any objects created from that class. Instead, we would use publicly accessible predicates of the code within that class directly.
Visual Prolog является полностью объектно-ориентированным языком.
Весь код, который разрабатывается для программы, помещается в класс. Это происходит само собой, даже если Вы не интересуетесь объектными свойствами языка. Это свойство обнаруживается в примере, разбираемом в этом руководстве. Код помещается в класс, называемый "family1", несмотря на то, что мы не используем объекты, порождаемые этим классом. Кроме того, вы этом классе мы используем общедоступные предикаты, находящиеся в других классах.


This tutorial will not handle the object oriented features of the language. Future tutorials would extend this concept, and even actually use the object oriented features.
В этом руководстве объектные свойства языка не используются. Объектные свойства языка используются в других руководствах.


==A Full Fledged Example: family1.prj6==
==Полностью работающий пример: family1.prj6==


'''Download:''' Source files of the example project used in this tutorial.<img alt src="../tut11/images/tut11_dot.gif" border="0" width="1" height="1">
'''Сгрузитe:''' [http://www.visual-prolog.com/vip70/tutorial/tut09/family1.zip Исходные Файлы] проекта, используемого в этом руководстве.


Let us now put all the knowledge we've gathered together to create our first Visual Prolog program. This will contain the same basic logic that was explored in the Tutorial "Fundamental Prolog. Part 2". All the code that is to be written for this tutorial is shown below. It has to be written into the file called ''family1.pro''. The actual code writing will be done using the Visual Prolog IDE (Integrated Development Environment). There are some more files that are needed for the tutorial, but those would automatically be generated and maintained by the IDE. The step by step instructions (using screenshots) for utilizing the IDE to develop the program will shortly follow.
Давайте теперь все полученные знания соберем вместе и создадим первую программу на языке системы Visual Prolog. Общая логика будет та же, что и рассмотренная в руководстве "Fundamental Prolog. Part 2". Текст программы приведен ниже. Его надо поместить в файл с именем ''family1.pro''. Реальный текст программы мы напишем с помощью среды IDE (Integrated Development Environment) системы  Visual Prolog. На самом деле файлов больше, чем требуется для этого руководства, но они будут созданы (и в дальнейшем будут поддерживаться) автоматически средой IDE. Пошаговая инструкция по использованию IDE (с фрагментами экрана) для разработки программы приводится ниже.


But first of all, let us acquaint ourselves with the main code of the program:
Но прежде всего ознакомимся с главным текстом программы:


<vip>implement family1
<vip>implement family1
    open core
open core


constants
constants
    className = "family1".
  className = "family1".
        classVersion = "$JustDate: $$Revision: $".
  classVersion = "$JustDate: $$Revision: $".


clauses
clauses
    classInfo(className, classVersion).
  classInfo(className, classVersion).


domains
domains
    gender = female(); male().
  gender = female(); male().


class facts - familyDB
class facts - familyDB
    person : (string Name, gender Gender).
  person : (string Name, gender Gender).
    parent : (string Person, string Parent).
  parent : (string Person, string Parent).


class predicates
class predicates
    father : (string Person, string Father) nondeterm anyflow.
  father : (string Person, string Father) nondeterm anyflow.
clauses
clauses
    father(Person, Father) :-
  father(Person, Father) :-
        parent(Person, Father),
    parent(Person, Father),
        person(Father, male()).
    person(Father, male()).


class predicates
class predicates
    grandFather : (string Person, string GrandFather) nondeterm anyflow.
  grandFather : (string Person, string GrandFather) nondeterm anyflow.
clauses
clauses
    grandFather(Person, GrandFather) :-
  grandFather(Person, GrandFather) :-
        parent(Person, Parent),
    parent(Person, Parent),
        father(Parent, GrandFather).
    father(Parent, GrandFather).


class predicates
class predicates
    ancestor : (string Person, string Ancestor) nondeterm anyflow.
  ancestor : (string Person, string Ancestor) nondeterm anyflow.
clauses
clauses
    ancestor(Person, Ancestor) :-
  ancestor(Person, Ancestor) :-
        parent(Person, Ancestor).
    parent(Person, Ancestor).
    ancestor(Person, Ancestor) :-
  ancestor(Person, Ancestor) :-
        parent(Person, P1),
    parent(Person, P1),
        ancestor(P1, Ancestor).
    ancestor(P1, Ancestor).


class predicates
class predicates
    reconsult : (string FileName).
  reconsult : (string FileName).
clauses
clauses
    reconsult(FileName) :-
  reconsult(FileName) :-
        retractFactDB( familyDB),
    retractFactDB( familyDB),
        file::consult(FileName, familyDB).
    file::consult(FileName, familyDB).


clauses
clauses
    run():-
  run():-
        console::init(),
    console::init(),
        stdIO::write("Load data\n"),
    stdIO::write("Load data\n"),
        reconsult("fa.txt"),
    reconsult("fa.txt"),
        stdIO::write("\nfather test\n"),
    stdIO::write("\nfather test\n"),
        father(X, Y),
    father(X, Y),
            stdIO::writef("% is the father of %\n", Y, X),
      stdIO::writef("% is the father of %\n", Y, X),
        fail.
    fail.
    run():-
  run():-
        stdIO::write("\ngrandFather test\n"),
    stdIO::write("\ngrandFather test\n"),
        grandFather(X, Y),
    grandFather(X, Y),
            stdIO::writef("% is the grandfather of %\n", Y, X),
      stdIO::writef("% is the grandfather of %\n", Y, X),
        fail.
    fail.
    run():-
  run():-
        stdIO::write("\nancestor of Pam test\n"),
    stdIO::write("\nancestor of Pam test\n"),
        X = "Pam",
    X = "Pam",
        ancestor(X, Y),
    ancestor(X, Y),
            stdIO::writef("% is the ancestor of %\n", Y, X),
      stdIO::writef("% is the ancestor of %\n", Y, X),
        fail.
    fail.
    run():-
  run():-
        stdIO::write("End of test\n").
    stdIO::write("End of test\n").
end implement family1
end implement family1


Строка 223: Строка 233:
mainExe::run(family1::run).</vip>
mainExe::run(family1::run).</vip>


===Step 1: Create a New Console Project in the IDE===
===Шаг 1: Создайте в IDE новый консольный проект===


'''Step 1a.''' After starting the Visual Prolog IDE, click on the New menu item from the Project menu.
'''Шаг 1a.''' После старта среды программирования Visual Prolog, выберите ''New'' из меню ''Project''.


[[Image:FundamentalVisualProlog1.jpg]]
[[Image:FundamentalVisualProlog1.jpg]]


'''Step 1b.''' A dialog would be presented to you. Enter all the relevant information. Ensure that the UI Strategy is ''Console'' and NOT ''GUI''.
'''Шаг 1b.''' Появляется диалог. Введите соответствующую информацию. Удостоверьтесь, что стратегия пользовательского интерфейса (UI Strategy) ''Console'', а '''НЕ''' ''GUI''.


[[Image:FundamentalVisualProlog2.jpg]]
[[Image:FundamentalVisualProlog2.jpg]]


===Step 2: Build an Empty Project===
===Шаг 2: Постройте пустой проект===


'''Step 2a.''' When the project is just created, the IDE would display the following project window. At this point in time, it does not have any clue about what files the project is dependent on. It does, however create the basic source code files for the project.
'''Шаг 2a.''' Когда проект только что создан, среда будет показывать проектное окно, как показано ниже. В этот момент пока никакой информации о том, от каких файлов зависит проект, нет. Однако основные тексты программ проектных файлов уже созданы.


'''Step 2b.''' Use the Build menu item of the Build menu, to compile and link the empty project to create an executable file which basically does not do anything. (At this point it time, it would not generate any errors either)
'''Шаг 2b.''' Выберите из меню ''Build'' позицию ''Build''. Пустой проект, который ничего не делает, будет построен. (В этот момент времени никаких сообщений о ошибках быть не должно).


While building the project, take a look at the messages that are dynamically displayed in the '''Messages''' window of the IDE. (In the case the '''Messages''' window is not visible, you can turn it on using the Window menu of the IDE) You will notice that the IDE intelligently pulls in ALL the necessary PFC (Prolog Foundation Classes) modules into your project. PFC classes are those classes which contains the basic functionality on which your programs would be built.
В процессе построения проекта посмотрите на сообщения, которые динамически появляются в окне '''Messages''' среды IDE. (Если окно '''Messages''' не видно, его можно вызвать на передний план, используя меню Window в среде IDE). Вы заметите, что среда IDE разумно включит в Ваш проект ВСЕ в необходимые модули PFC (Prolog Foundation Classes). Классы PFC являются теми классами, которые содержат поддержку функционирования программ, которые Вы строите.


===Step 3: Populate the family1.pro with the Actual Code===
===Шаг 3: Поместите в файл family1.pro актуальный код===


'''Step 3a.''' The default code inserted by the IDE is very basic and does not do anything useful. You would need to delete the entire code and copy and paste the actual code of family1.pro (given in this tutorial) into that window.
'''Шаг 3a.''' Начальный код, созданный самой средой IDE имеет самый общий вид и сам по себе  не делает ничего полезного. Вам необходимо удалить этот код полностью и скопировать (через Copy и Paste) полностью (актуальный на данный момент) код программы family1.pro (приведенный в этом руководстве) в окно редактора.


[[Image:FundamentalVisualProlog5.jpg]]
[[Image:FundamentalVisualProlog5.jpg]]


'''Step 3b.''' After you perform the copy-paste operation, the family1.pro window would now look as shown below:
'''Шаг 3b.''' После выполнения операции copy-paste окошко с текстом файла family1.pro должно выглядеть так, как показано ниже:


[[Image:FundamentalVisualProlog6.jpg]]
[[Image:FundamentalVisualProlog6.jpg]]


===Step 4: Rebuild the Code===
===Шаг 4: Перестроение кода проекта===


Re-invoke the Build menu command, and the IDE will notice that the source code has changed, and will take measures to appropriately recompile the correct sections. If you use the Build All menu command, then ''ALL'' the modules would be recompiled. For large programs this can consume some time. Build All is often used ONLY at the end of a series of smaller compilations; just to ensure that everything is in ship-shape order.
Повторите вызов команды меню '''Build'''. IDE теперь уведомит, что исходный текст поменялся, и выполнит все необходимые действия для перекомпиляции соответствующих разделов. Если Вы вызовите команду меню Rebuild All, тогда ''ВСЕ'' модули проекта будут перекомпилированы. В случае больших проектов это может занимать значительное время. Rebuild All обычно используется в качестве финальной фазы после ряда небольших изменений и соответствующих компиляций, дабы удостовериться в том, что все в полном порядке.


During the ''Build'' process, the IDE not only does the compilation; but it also determines whether the project may need any other missing PFC modules, and inserts those and re-starts the compilation process if required. This can be seen in the messages appearing in the Messages window, where you'll notice the IDE was forced to build the project ''twice'' because of some additional include statements.
В процессе выполнения команды ''Build'' среда IDE выполняет не только компиляцию. В это же время выясняется, нужны ли проекту другие модули из набора PFC (Prolog Faundation Classes), и, если это так, то такие модули добавляются и вызывается повторная перекомпиляция, если необходимо. Этот процесс можно наблюдать по сообщениям, появляющимся в окне сообщений (Message Window). Можно увидеть как IDE вызвало построение проекта дважды, поскольку были обнаружены директивы "include", повлиявшие на это.


[[Image:FundamentalVisualProlog7.jpg]]
[[Image:FundamentalVisualProlog7.jpg]]


===Step 5: Execute the Program===
===Шаг 5: Исполнение Программы===


We now have our first true application complied using Visual Prolog. In order to test the application we've just compiled, you can run the program using the Build | Run ''in'' Window menu command. However, in our case this would result in an error. The reason is that our little program is trying to read a text file (fa.txt) for its functioning. That text file contains the actual data regarding persons which our program will process. We'll come to the syntax of that file a bit later.
Теперь у нас есть приложение, успешно откомпилированное с помощью Visual Prolog. Для того, чтобы проверить работу приложения, которое только что построено, можно запустить ее выполнение, вызвав команду из меню ''Build | Run in Window''. Но в нашем случае это приведет к ошибке. Причина заключается в том, что наша маленькая программа для обеспечения своей работы пытается читать текстовый файл ''fa.txt''. Этот текстовый файл содержит содержит записи относительно лиц, которые (записи) программа должна обработать. Вопросов синтаксита представления данных в этом файле мы коснемся позже.


We need to now write that text file using some text editor and place it in the same directory where the executable is now residing. Normally, the executable would reside in the sub-folder called exe from the main project folder.
Сейчас нам необходимо написать этот текстовый файл с использованием текстового редактора и поместить его в ту же директорию, где располагается сама исполняемая программа. Обычно исполняемая программа помещается в поддиректории проектной директории, имеющей имя EXE.


Let us create such a file using the IDE itself. Use the File | New menu command. The Create Project Item window will appear. Select Text file from the list on the left hand side. Then select the appropriate directory where the file is to reside (The same directory where the executable file family1.exe is present). Then, give the filename ''fa.txt'' and click on the Create button of the dialog. Till the filename is given, the Create button would be grayed (disabled). Make sure that the checkbox: Add to project as module is checked on. The advantage of adding it to the project is that it would be available for any subsequent debugging, etc.
Давайте создадим такой файл сами с использованием среды IDE. Вызовите команду меню ''File | New''. Появляется окно создания новой сущности ''Create Project Item''. Выберите Текстовый файл (Text File) в списке слева. Затем выберите директорию, где будет размещаться файл (та же директория, где располагается исполняемое приложение family1.exe). Присвойте теперь имя ''fa.txt'' файлу и нажмите кнопку Create (создать). До тех пор, пока имя файла не введено, кнопка Create (создать) будет неактивна (надпись серого цвета). Удостоверьтесь, что флажот ''Add to project as module'' (добавить в проект на правах модуля) включен. Полезность включения файла в проект заключается в том, что это будет удобно для последующей отладки и других действий.
 
The contents of the file would be as follows:


Файл следует заполнить текстом следующего содержания:
<vip>clauses
<vip>clauses
     person("Judith",female()).
     person("Judith",female()).
        person("Bill",male()).
    person("Bill",male()).
        person("John",male()).
    person("John",male()).
        person("Pam",female()).
    person("Pam",female()).
        parent("John","Judith").
    parent("John","Judith").
        parent("Bill","John").
    parent("Bill","John").
        parent("Pam","Bill").</vip>
    parent("Pam","Bill").</vip>


Though it is a data file, you would notice that the syntax of the file is VERY similar to regular Prolog code! Even though the program is now compiled and is now in a binary format; you can change the output by simply changing the crucial data the program is processing. And that data is written into this text file using the same syntax as Prolog. Thus Visual Prolog emulates the ''dynamic'' code writing capability of traditional Prolog. Albeit, not all features are available but at least complicated domains such as those in this example can definitely be given.
Несмотря на то, что это файл данных, Вы заметите, что синтаксис файла очень похож на обычный код Пролога! Даже несмотря на то, что программа теперь откомпилирована и представлена в двоичном формате, Вы можете менять ход и результаты ее исполнения, изменяя данные, которые программа обрабатывает. И эти данные записываются в этот текстовый файл с использованием того же самого синтаксиса Пролога. Visual Prolog таким образом эмулирует свойство использования динамически изменяемого кода, характерное для традиционного пролога. Хотя не все свойства использования динамически изменяемого кода традиционного Пролога воспроизводятся, но достаточно сложные структуры данных (или не сложные, как в этом примере) могут быть использованы.


The syntax used in fa.txt MUST be compatible with the domain definitions of the program. E.g. The functor used for defining a person MUST be ''person'' and not anything else; else appropriate compound domain representing that functor would not get initialized. (We had covered the topic of functors and compound domains in an earlier tutorial).
Синтаксис файла fa.txt ДОЛЖЕН быть совместим с определениями доменов в программе. Например, функтор, применяемый для определения персоны ДОЛЖЕН быть ''person'' и никак иначе. В противном случае соответствующий составной домен не будет инициализирован. (Понятия функторов и составных доменов рассматриваются в других руководствах).


Now, when you run the program using the Build | Run in the Window menu command, this is what you'll see:
Теперь, запустив программу по команде меню '''Build | Run in the Window''' вы увидите:


[[Image:FundamentalVisualProlog8.jpg]]
[[Image:FundamentalVisualProlog8.jpg]]


The program processes the information in the file fa.txt and works out the logical sequence of the parentages of people given there.
Программа обрабатывает данные, помещенные в файл fa.txt и порождает логические связи персон, данные о которых там помещены.


===A Round up of the Program===
===Рассмотрение программы===


The logic of the program is very simple. We had discussed it in an earlier tutorial, where we explained how the correct representation of data can help yield the appropriate results in a Prolog program. Let us now discuss the manner in which this logic works.
Логика программы очень проста. Мы ее уже обсуждали в других руководствах, где рассматривалось как корректное представление данных может помочь построить эффективную программу на Прологе. Давайте теперь обратим внимание на логику используемого метода.


At start up, the main predicate will be invoked directly. This in turns branches off to the run predicate. The first thing the run predicate will do is to read in the data which is written in the file fa.txt. Once the data is in place, the program systematically progresses from one test to another to process the data and write out the conclusions of that test onto the console.
При старте непосредственно будет вызван главный предикат. Здесь все построена вокруг предиката ''run''. Предикат ''run'' прежде всего зачитывает данные, записанные в файле ''fa.txt''. После загрузки данных, программа систематически переходит от одной проверки к другой, обрабатывая данные, и выводит на консоль выводы на базе этих проверок.


The mechanisms for processing the data are standard mechanisms of backtracking and recursions. This tutorial is not the place for a detailed explanation of how Prolog works, but here is a short summary of the internal workings.
Обработка данных основывается на стандартных механизмах отката и рекурсивных вызовов. В этом руководстве детально не рассматривается как работает Пролог, но приведем краткое описание работы.


When the run predicate is calling the father predicate, it does not stop at the first satisfactory ending of the father predicate. The invoking of a fail at the end of the predicate, forces the Prolog engine to seek another pass through the father predicate. Such a behavior is called ''backtracking'', because the Prolog engine literally seems to backtrack over code which had earlier executed.
Когда предикат ''run'' вызывает предикат ''father'', он не ограничивается первым удовлетворяющим решением предиката ''father''. Применение предиката fail в конце последовательности понуждает механизм Пролога к поиску следующего прохода предиката ''father''. Такое поведение, называется ''backtracking'' (откат), поскольку кажется, что механизм Пролога буквально перепрыгивает назад, минуя ранее исполненный код.


This happens recursively (i.e. as a repetitive process or cyclically), and at each cycle the father predicate yields the next result. Eventually all the possible definitions of "fathers" given in the data are exhausted, and the run predicate would have no choice but carry over to the next clause body of the run predicate.
Это происходит рекурсивно (то есть как повтояющийся или циклический процесс) и в каждом цикле предикат ''father'' вырабатывает новый результат. В итоге все возможные определения "отцов", представленные данными исчерпываются и у предиката ''run'' нет другого выхода как перейти к следующему своему клаузу.


The father predicate (as well as some other predicates) has been declared to be non-deterministic. The keyword nondeterm can be used to declare non-deterministic predicates; i.e. predicates that can yield multiple answers using backtracking.
Предикат ''father'' (так же как и ряд других предикатов) объявлен как нетерминированный. Для обозначения недетерминированных предикатов используется ключевое слово '''nondeterm''', обозначающее, что предикат может вырабатывать множество решений посредством бэктрекинга (отката).


If the no keyword is used in a predicate declaration, then the predicate has got the procedure mode, which can give only one solution, and the father predicate will stop after yielding the first result and cannot be re-entered into. Also, one must be careful in the usage of the cut (!) in the clause bodies of such non-deterministic predicates. If there is a cut at the end of the clause body of the father predicate, yet again; the predicate will yield only one solution (even if it has been declared to be non-deterministic).
Если никакое ключевое слово в объявлении предиката не используется, то предикат получает режим процедуры, которая может выработать только одно решение, и предикат ''father'' прекратил бы работу после получения первого же результата и не смог бы быть вызван повторно. Заметим, следует быть очень осторожными в использовании отсечения (cut), обозначаемого как '''!''', в клаузах таких недетерминированных предикатов. Если в конце клаузы предиката ''father'' помещено отсечение, то предикат выработает только одно решение (даже если он объявлен как недетерминированный).


The anyflow flow-pattern tells the compiler that the parameters that are given for the predicate could be either free or bound, with no restrictions whatsoever. That means, with the same definition of the father predicate, it would be possible to both yield the father's name (in case the name of the offspring was bound) OR the offspring's name (in case the name of the father was bound). It can also handle situations where both are bound; in which case the predicate will check if such a relationship exists in the data.
Квалификатор направлений ввода-вывода ''anyflow'' говорит компилятору о том, что параметры, назначенные предикату могут быть как связанными, так и свободными, без каких либо ограничений. Это означает, что используя одну и ту же декларацию предиката ''father'', можно будет получать как имя отца (в случае, если имя потомка связано) так и имя потомка (в случае, если имя отца связано). Ситуация, когда оба параметра связаны, также обрабатывается - и в этом случае проверяется существует ли отношение между данными, представленными параметрами.


And; to state the obvious; the anyflow flow pattern can also accommodate a situation where both the parameters of the father predicate are free variables. In such a case, the father predicate will yield the various combinations of father+offspring present in the data the program has learnt (through consulting the fa.txt file) to the enquirer. The last usage is what is performed in the run predicate, as seen in the code snippet below. You would notice that the variables X and Y given to the father predicate are not bound when the father predicate is called.
И, наконец, квалификатор ''anyflow'' включает ситуацию, когда оба параметра предиката ''father'' являются свободными переменными. В этом случае, предикат  ''father'' вернет в ответ на запрос различные комбинации отношений родитель-потомок, предусмтотренные в программе (представленные в загружаемом файле ''fa.txt''). Последний вариант как раз и использован в предикате ''run'', как видно во фрагменте, приведенном ниже. Можно заметить, что переменные X и Y переданные предикату ''father'' не связаны при его вызове.


<vip>clauses
<vip>clauses
Строка 318: Строка 327:
         fail.</vip>
         fail.</vip>


==Conclusion==
==Заключение==
 
In this lesson we learnt that programs written in Visual Prolog are often very similar to those written in traditional Prolog. There are several keywords that are used to differentiate the various parts of a Visual Prolog source code. Though Visual Prolog is an object-oriented language, it is possible to develop code which avoids the language's object-oriented features. A complete console-based application (family1) was developed in this tutorial, which explains, how to create such a program.


We also found that it is possible to emulate the dynamic working of a traditional Prolog program by keeping part of the code as a data file outside the main binary compiled application. The syntax of such a data file closely follows Prolog syntax.
На этом материале мы познакомились с тем, что программы, написанные в системе программирования Visual Prolog, часто очень похожи на программы, написанные в традиционном Прологе. Некоторый набор ключевых слов используется для обозначения различий частей исходного кода на языке системы Visual Prolog. Хотя Visual Prolog является объектно-ориентированным языком, сохраняется возможность  разработки кода, не использующего свойства объектной ориентированности. Было показано работающее приложение консольного типа, которое поясняет как создавать подобные программы.


==References==
Мы увидели, кроме того, как эмулируется работа с динамическими данными, характерная для традиционного Пролога. Это делается путем помещения части кода в виде внешнего файла, отделенного от программы, представленной в двоичном формате. Синтаксис данных такого рода полностью соответствует синтаксису Пролога.


==Ссылки==
[[en:Fundamental Visual Prolog]]
[[Категория:VipРуководства]]
[[Категория:VipРуководства]]

Текущая версия на 09:00, 25 декабря 2007

Основы Visual Prolog


В этом руководстве мы представляем программу, разработанную на платформе системы программирования Visual Prolog. Алгоритмы, используемые в руководстве , - те же, что и в руководстве "Fundamental Prolog" (Часть 2).

Отличия между Visual Prolog и традиционным Прологом

Различия между традиционным Прологом и Visual Prolog можно провести по следующим категориям:

  • Различия в структуре программы:
    Различия между Visual Prolog и традиционным Прологом имеются, но они не существенны. Они сводятся к пониманию того, как различаются декларации (declarations) от определений (definitions), а также к явному выделению цели Goal. Все это поддержано специальными ключевыми словами.
  • Файловая структура программы::
    Visual Prolog предоставляет возможности структуризации программ с использованием файлов различного типа.
  • Границы видимости:
    Программа системы Visual Prolog может поддерживать функционирование, располагающееся в различных модулях с использованием концепции идентификация пространств.
  • Объектная ориентированность:
    Программа на языке Visual Prolog может быть написана как объектно ориентированная программа с использованием классических свойств объектно-ориентированной парадигмы.

Различия в структуре программ

Декларации и Определения

В Прологе, когда необходимо использовать предикат, то это делается без каких-либо предварительных указаний движку Пролога о таких намерениях. Например, в предыдущих руководствах клауз предиката grandFather (дедушка) был непосредственно записан с использованием традиционной для Пролога конструкции голова-тело. Мы не беспокоились о том, чтобы в тексте программы предупредить движок явно, что такая конструкция придиката потребуется.

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

Однако, в Visual Prolog, перед написанием кода для тела клауза предиката мы должны сначала объявить о существовании такого предиката компилятору. Аналогично, перед использованием любых доменов они должны быть объявлены и представлены компилятору.

Причиной такой необходимости в предупреждениях является попытка как можно раньше обнаружить возможность исключений периода исполнения.

Под "исключениями периода исполнения (runtime exceptions)", мы понимаем события, возникающие только во время исполнения программы. Например, если Вы вознамерились использовать целое число в качестве аргумента функтора, а вместо этого вы по ошибке использовали вещественное число, то в процессе исполнения возникла бы ошибка периода исполнения (в программах, написанных для ряда компиляторов, не не для Visual Prolog) и программа в этом случае завершилась бы неуспешно.

Когда Вы объявляете предикаты или доменты, которые определены, то появляется своего рода позиционная грамматика (какому домену принадлежит какой аргумент), доступная компилятору. Более того, когда Visual Prolog выполняет компиляцию, он тщательно проверяет программу на наличие таких грамматических ошибок, наряду с другими.

Благодяря этому свойству Visual Prolog, повышается конечная эффективность программиста. Программист не должен ждать, когда реально работаящая программа совершит ошибку. Действительно, те из Вас, кто имеет опыт в программировании, понимает насколько этот код безопасен. Часто конкретная последовательность событий, приводящая к исключению периода исполнения кажется настолько неуловимой, что ошибка может проявиться через много лет, или она может заявить о себе в критической ситуации и привести к непредсказуемым последствиям!

Все это автоматически ведет к тому, что компилятор должен получать точные инструкции по поводу предикатов и доменов в виде соответствующих объявлений, которые должны предшествовать определениям.

Ключевые слова

Программа на Visual Prolog, представляемая кодом, разделяется ключевыми словами на секции разного вида путем использования ключевых слов, предписывающих компилятору, какой код генерировать. К примеру, есть ключевые слова, обозначающие различие между декларациями и определениями предикатов и доменов. Обычно, каждой секции предшествует ключевое слово. Ключевых слов, обозначающих окончание секции, нет. Наличие другого ключевого слова обозначает окончание предыдущей секции и начало другой.

Исключением из этого правила являются ключевые слова implement и end implement. Код, содержащийся между этими ключевыми словами, есть код, который относится к конкретному классу. Те, кто не понимает концепцию класса, может пока (в рамках этого руководства) представлять себе его как модуль или раздел какой-то программы более высокого уровня.

В рамках этого руководства мы представляем только часть ключевых слов, приведенных ниже. Мы также объясняем назначение этих ключевых слов, а конкретный синтаксис легко может быть изучен по документации. В Visual Prolog есть и другие ключевые слова, они упоминаются в других рукодствах.

В этом руководстве используются следующие ключевые слова:

implement и end implement

Среди всех ключевых слов, обсуждаемых здесь, это единственные ключевые слова, используемые парно. Visual Prolog рассматривает код, помещенный между этими ключевыми словами, как код, принадлежащий одному классу. За ключевым словом implement обязательно ДОЛЖНО следовать имя класса.

open

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

constants

Это ключевое слово исползуется для обозначения секции кода, котора определяет неоднократно используемые значения, применяемые в коде. Например, если строковый литерал "PDC Prolog" предполагается использовать в различных местах кода, тогда можно единожды определить мнемоническое (краткое, легко запоминаемое слово) для использовани в таких местах:
constants
pdc="PDC Prolog".
Заметьте, что определение константы завершается точкой (.). В отличие от переменных Пролога константы должны начинаться со строчной буквы (нижний регистр).

domains

Это ключевое слово используется для обозначения секции, объявляющей домены, которые будут использованы в коде. Синтаксис таких объявлений позволяет порождать множество вариантов объявлений доменов, используемых в тексте программы. В пределах этого руководства начального уровня мы не будем вдаваться в детали объаявлений доменов.
Вообще говоря, объявляется функтор, который будет использоваться в качестве домена и ряд доменов, которые используются в качестве его аргументов. Функторы и составные домены рассматриваются в соответствующем руководстве.

class facts

Это ключевое слово представляет секцию, в которой объявляются факты, которые будут в дальнейшем использоваться в тексте программы. Каждый факт объявляется как имя, используемое для обозначения факта, и набор аргументов, каждый из которых должен соответствовать либо стандартному (предопределенному), либо объявленному домену.

class predicates

Эта секция содержит объявления предикатов, которые определяются в тексте программы в разделе clauses. И опять, объявление предиката - это имя, которое присваивается предикату, и набор аргументов, каждый из которых должен соответствовать либо стандартному (предопределенному), либо объявленному домену.

clauses

Среди всех разделов, существующих в тексте программ на Visual Prolog, это единственный раздел, который близко совпадает с традиционными программами на Прологе. Он содержит конкретные определения объявленных в разделе class predicates предикатов, причем синтаксически им соответствующие.

goal

Этот раздел определяет главную точку входа в программу на языке системы Visual Prolog. Более детальное описание приводится ниже.

Goal (цель)

В традиционном Прологе, как только какой-либо предикат определен в тексте, Пролог-машине может быть предписано выполнение программы, начиная с этого предиката. В Visual Prolog это не так. Будучи компилятором, он отвечает за генерацию эффективного исполняемого кода написанной программы. Этот код исполняется не в то же время, когда компилятор порождает код. Поэтому компилятору надо знать заранее точно, с какого предиката начнется исполнение программы. Благодаря этому, когда программа вызывается на исполнение, она начинает работу с правильной начальной точки. Как можно уже догадаться, откомпилированная программа может работать независимо от компилятора Visual Prolog и без участия среды IDE.

Для того, чтобы это стало возможным, введен специальный раздел, обозначенный ключевым словом Goal. Его можно представлять как особый предикат, не имеющий аргументов. Это предикат - именно тот предикат, с которого вся программа начинает исполняться.

Файловое структурирование программ

Необходимость помещения всех частей большой программы в один файл несомненно является неудобством. Это ведет к тому, что программа становится нечитаемой и иногда неправильной. Visual Prolog предусматривает возможность деления текста программы на отдельные файлы, используя среду IDE (Integrated Development Environment) и, кроме того, IDE позволяет помещать логически связанные фрагменты текста программы в отдельные файлы. Несмотря на такое разделение программы на части, сущности, находящиеся в общем использовании, доступны.

Например, если имеется домен, который используется несколькими файлами, то объявление домена делается в отдельном файле и к этому файлу из других файлов существует доступ.

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

Расширение Области Видимости

В Visual Prolog текст программы разделен на отдельные части, какждая часть определяется как класс (class). В объектно-ориентированных языках программирования, класс - это пакет кода и ассоциированные с ним данные. Это требует длительных объяснений и сделано в других руководствах. Те, кто не знаком с объектно-ориентированным программированием, может представлять себе класс нестрого как синоним понятия модуль. В Visual Prolog каждый класс обычно помещается в отдельный файл.

В процессе исполнения программы, часто бывает так, что программе может потребоваться вызвать предикат, который определен в другом классе (файле). Аналогично, данные (константы) или домены могут быть востребованы в другом файле.

Visual Prolog позволяет делать такие взаимные ссылки на предикаты или данные используя так называемую концепцию области видимости (scope access). Это может стать понятным на примере. Предположим имеется предикат, называемый pred1 и определенный в классе называемом class1 (который помещается в другом файле, согласно стратегии среды IDE), и мы хотим вызвать этот предикат в теле клауза некоторого другого предиката pred2, находящегося в другом файле (скажем, class2). Тогда вот как предикат pred1 должен бы быть вызван в теле клауза предиката pred2 :

clauses
  pred3:-
    ...
    !.
 
  pred2:-
    class1::pred1, % pred1 неизвестен в этом файле. 
                   % Он определен в другом файле, 
                   % поэтому требуется квалификатор класса.
    pred3,
    ...

В приведенном примере видно, что тело клауза предиката pred2 вызывает два предиката pred1 и pred3. Поскольку pred1 определен в другом файле (где определен класс class1), постольку слово class1 с символом :: (два двоеточия) предшествует слову pred1. Это можно назвать как квалификатор класса. Но предикат pred3 определен внутри того же самого файла, что и предикат pred2. Поэтому здесь нет необходимости использовать class2:: перед вызовом предиката pred3.

Технически такое поведение объясняется следующим: Область видимости (scope visibility) предиката pred3 находится внутри той же области, где находится предикат pred2. Поэтому здесь не нужно показывать, что pred3 и pred2 находятся в одном классе. Компилятор автоматически увидит определение предиката pred3 внутри той же области, в классе class2.

Область видимости конкретного класса лежит внутри границ, где класс определен (то есть в интервале между ключевыми словами implement - end implement ). Предикаты, определенные здесь, могут вызывать друг друга без квалификатора класса и двойного двоеточия (::).

Область видимости класса может быть расширена использованием ключевого слова open. Это ключевое слово информирует компилятор о том, что в этот класс должны быть "доставлены" имена (предикатов / констант / доменов), которые были определены в других файлах. Если область видимости расширена, то необходимости использования квалификатора класса (с двойным двоеточием) нет.

open class1
  ...
 
clauses
  pred3:-
    ...
    !.
 
  pred2:-
    pred1, % Внимание: квалификатор "class1::" больше не нужен,
           % поскольку область видимости расширена 
           % использованием ключевого слова 'open'
    pred3,
    ...

Объектная ориентированность

Visual Prolog является полностью объектно-ориентированным языком.

Весь код, который разрабатывается для программы, помещается в класс. Это происходит само собой, даже если Вы не интересуетесь объектными свойствами языка. Это свойство обнаруживается в примере, разбираемом в этом руководстве. Код помещается в класс, называемый "family1", несмотря на то, что мы не используем объекты, порождаемые этим классом. Кроме того, вы этом классе мы используем общедоступные предикаты, находящиеся в других классах.

В этом руководстве объектные свойства языка не используются. Объектные свойства языка используются в других руководствах.

Полностью работающий пример: family1.prj6

Сгрузитe: Исходные Файлы проекта, используемого в этом руководстве.

Давайте теперь все полученные знания соберем вместе и создадим первую программу на языке системы Visual Prolog. Общая логика будет та же, что и рассмотренная в руководстве "Fundamental Prolog. Part 2". Текст программы приведен ниже. Его надо поместить в файл с именем family1.pro. Реальный текст программы мы напишем с помощью среды IDE (Integrated Development Environment) системы Visual Prolog. На самом деле файлов больше, чем требуется для этого руководства, но они будут созданы (и в дальнейшем будут поддерживаться) автоматически средой IDE. Пошаговая инструкция по использованию IDE (с фрагментами экрана) для разработки программы приводится ниже.

Но прежде всего ознакомимся с главным текстом программы:

implement family1
open core
 
constants
  className = "family1".
  classVersion = "$JustDate: $$Revision: $".
 
clauses
  classInfo(className, classVersion).
 
domains
  gender = female(); male().
 
class facts - familyDB
  person : (string Name, gender Gender).
  parent : (string Person, string Parent).
 
class predicates
  father : (string Person, string Father) nondeterm anyflow.
clauses
  father(Person, Father) :-
    parent(Person, Father),
    person(Father, male()).
 
class predicates
  grandFather : (string Person, string GrandFather) nondeterm anyflow.
clauses
  grandFather(Person, GrandFather) :-
    parent(Person, Parent),
    father(Parent, GrandFather).
 
class predicates
  ancestor : (string Person, string Ancestor) nondeterm anyflow.
clauses
  ancestor(Person, Ancestor) :-
    parent(Person, Ancestor).
  ancestor(Person, Ancestor) :-
    parent(Person, P1),
    ancestor(P1, Ancestor).
 
class predicates
  reconsult : (string FileName).
clauses
  reconsult(FileName) :-
    retractFactDB( familyDB),
    file::consult(FileName, familyDB).
 
clauses
  run():-
    console::init(),
    stdIO::write("Load data\n"),
    reconsult("fa.txt"),
    stdIO::write("\nfather test\n"),
    father(X, Y),
      stdIO::writef("% is the father of %\n", Y, X),
    fail.
  run():-
    stdIO::write("\ngrandFather test\n"),
    grandFather(X, Y),
      stdIO::writef("% is the grandfather of %\n", Y, X),
    fail.
  run():-
    stdIO::write("\nancestor of Pam test\n"),
    X = "Pam",
    ancestor(X, Y),
      stdIO::writef("% is the ancestor of %\n", Y, X),
    fail.
  run():-
    stdIO::write("End of test\n").
end implement family1
 
goal
mainExe::run(family1::run).

Шаг 1: Создайте в IDE новый консольный проект

Шаг 1a. После старта среды программирования Visual Prolog, выберите New из меню Project.

FundamentalVisualProlog1.jpg

Шаг 1b. Появляется диалог. Введите соответствующую информацию. Удостоверьтесь, что стратегия пользовательского интерфейса (UI Strategy) Console, а НЕ GUI.

FundamentalVisualProlog2.jpg

Шаг 2: Постройте пустой проект

Шаг 2a. Когда проект только что создан, среда будет показывать проектное окно, как показано ниже. В этот момент пока никакой информации о том, от каких файлов зависит проект, нет. Однако основные тексты программ проектных файлов уже созданы.

Шаг 2b. Выберите из меню Build позицию Build. Пустой проект, который ничего не делает, будет построен. (В этот момент времени никаких сообщений о ошибках быть не должно).

В процессе построения проекта посмотрите на сообщения, которые динамически появляются в окне Messages среды IDE. (Если окно Messages не видно, его можно вызвать на передний план, используя меню Window в среде IDE). Вы заметите, что среда IDE разумно включит в Ваш проект ВСЕ в необходимые модули PFC (Prolog Foundation Classes). Классы PFC являются теми классами, которые содержат поддержку функционирования программ, которые Вы строите.

Шаг 3: Поместите в файл family1.pro актуальный код

Шаг 3a. Начальный код, созданный самой средой IDE имеет самый общий вид и сам по себе не делает ничего полезного. Вам необходимо удалить этот код полностью и скопировать (через Copy и Paste) полностью (актуальный на данный момент) код программы family1.pro (приведенный в этом руководстве) в окно редактора.

FundamentalVisualProlog5.jpg

Шаг 3b. После выполнения операции copy-paste окошко с текстом файла family1.pro должно выглядеть так, как показано ниже:

FundamentalVisualProlog6.jpg

Шаг 4: Перестроение кода проекта

Повторите вызов команды меню Build. IDE теперь уведомит, что исходный текст поменялся, и выполнит все необходимые действия для перекомпиляции соответствующих разделов. Если Вы вызовите команду меню Rebuild All, тогда ВСЕ модули проекта будут перекомпилированы. В случае больших проектов это может занимать значительное время. Rebuild All обычно используется в качестве финальной фазы после ряда небольших изменений и соответствующих компиляций, дабы удостовериться в том, что все в полном порядке.

В процессе выполнения команды Build среда IDE выполняет не только компиляцию. В это же время выясняется, нужны ли проекту другие модули из набора PFC (Prolog Faundation Classes), и, если это так, то такие модули добавляются и вызывается повторная перекомпиляция, если необходимо. Этот процесс можно наблюдать по сообщениям, появляющимся в окне сообщений (Message Window). Можно увидеть как IDE вызвало построение проекта дважды, поскольку были обнаружены директивы "include", повлиявшие на это.

Файл:FundamentalVisualProlog7.jpg

Шаг 5: Исполнение Программы

Теперь у нас есть приложение, успешно откомпилированное с помощью Visual Prolog. Для того, чтобы проверить работу приложения, которое только что построено, можно запустить ее выполнение, вызвав команду из меню Build | Run in Window. Но в нашем случае это приведет к ошибке. Причина заключается в том, что наша маленькая программа для обеспечения своей работы пытается читать текстовый файл fa.txt. Этот текстовый файл содержит содержит записи относительно лиц, которые (записи) программа должна обработать. Вопросов синтаксита представления данных в этом файле мы коснемся позже.

Сейчас нам необходимо написать этот текстовый файл с использованием текстового редактора и поместить его в ту же директорию, где располагается сама исполняемая программа. Обычно исполняемая программа помещается в поддиректории проектной директории, имеющей имя EXE.

Давайте создадим такой файл сами с использованием среды IDE. Вызовите команду меню File | New. Появляется окно создания новой сущности Create Project Item. Выберите Текстовый файл (Text File) в списке слева. Затем выберите директорию, где будет размещаться файл (та же директория, где располагается исполняемое приложение family1.exe). Присвойте теперь имя fa.txt файлу и нажмите кнопку Create (создать). До тех пор, пока имя файла не введено, кнопка Create (создать) будет неактивна (надпись серого цвета). Удостоверьтесь, что флажот Add to project as module (добавить в проект на правах модуля) включен. Полезность включения файла в проект заключается в том, что это будет удобно для последующей отладки и других действий.

Файл следует заполнить текстом следующего содержания:

clauses
    person("Judith",female()).
    person("Bill",male()).
    person("John",male()).
    person("Pam",female()).
    parent("John","Judith").
    parent("Bill","John").
    parent("Pam","Bill").

Несмотря на то, что это файл данных, Вы заметите, что синтаксис файла очень похож на обычный код Пролога! Даже несмотря на то, что программа теперь откомпилирована и представлена в двоичном формате, Вы можете менять ход и результаты ее исполнения, изменяя данные, которые программа обрабатывает. И эти данные записываются в этот текстовый файл с использованием того же самого синтаксиса Пролога. Visual Prolog таким образом эмулирует свойство использования динамически изменяемого кода, характерное для традиционного пролога. Хотя не все свойства использования динамически изменяемого кода традиционного Пролога воспроизводятся, но достаточно сложные структуры данных (или не сложные, как в этом примере) могут быть использованы.

Синтаксис файла fa.txt ДОЛЖЕН быть совместим с определениями доменов в программе. Например, функтор, применяемый для определения персоны ДОЛЖЕН быть person и никак иначе. В противном случае соответствующий составной домен не будет инициализирован. (Понятия функторов и составных доменов рассматриваются в других руководствах).

Теперь, запустив программу по команде меню Build | Run in the Window вы увидите:

FundamentalVisualProlog8.jpg

Программа обрабатывает данные, помещенные в файл fa.txt и порождает логические связи персон, данные о которых там помещены.

Рассмотрение программы

Логика программы очень проста. Мы ее уже обсуждали в других руководствах, где рассматривалось как корректное представление данных может помочь построить эффективную программу на Прологе. Давайте теперь обратим внимание на логику используемого метода.

При старте непосредственно будет вызван главный предикат. Здесь все построена вокруг предиката run. Предикат run прежде всего зачитывает данные, записанные в файле fa.txt. После загрузки данных, программа систематически переходит от одной проверки к другой, обрабатывая данные, и выводит на консоль выводы на базе этих проверок.

Обработка данных основывается на стандартных механизмах отката и рекурсивных вызовов. В этом руководстве детально не рассматривается как работает Пролог, но приведем краткое описание работы.

Когда предикат run вызывает предикат father, он не ограничивается первым удовлетворяющим решением предиката father. Применение предиката fail в конце последовательности понуждает механизм Пролога к поиску следующего прохода предиката father. Такое поведение, называется backtracking (откат), поскольку кажется, что механизм Пролога буквально перепрыгивает назад, минуя ранее исполненный код.

Это происходит рекурсивно (то есть как повтояющийся или циклический процесс) и в каждом цикле предикат father вырабатывает новый результат. В итоге все возможные определения "отцов", представленные данными исчерпываются и у предиката run нет другого выхода как перейти к следующему своему клаузу.

Предикат father (так же как и ряд других предикатов) объявлен как нетерминированный. Для обозначения недетерминированных предикатов используется ключевое слово nondeterm, обозначающее, что предикат может вырабатывать множество решений посредством бэктрекинга (отката).

Если никакое ключевое слово в объявлении предиката не используется, то предикат получает режим процедуры, которая может выработать только одно решение, и предикат father прекратил бы работу после получения первого же результата и не смог бы быть вызван повторно. Заметим, следует быть очень осторожными в использовании отсечения (cut), обозначаемого как !, в клаузах таких недетерминированных предикатов. Если в конце клаузы предиката father помещено отсечение, то предикат выработает только одно решение (даже если он объявлен как недетерминированный).

Квалификатор направлений ввода-вывода anyflow говорит компилятору о том, что параметры, назначенные предикату могут быть как связанными, так и свободными, без каких либо ограничений. Это означает, что используя одну и ту же декларацию предиката father, можно будет получать как имя отца (в случае, если имя потомка связано) так и имя потомка (в случае, если имя отца связано). Ситуация, когда оба параметра связаны, также обрабатывается - и в этом случае проверяется существует ли отношение между данными, представленными параметрами.

И, наконец, квалификатор anyflow включает ситуацию, когда оба параметра предиката father являются свободными переменными. В этом случае, предикат father вернет в ответ на запрос различные комбинации отношений родитель-потомок, предусмтотренные в программе (представленные в загружаемом файле fa.txt). Последний вариант как раз и использован в предикате run, как видно во фрагменте, приведенном ниже. Можно заметить, что переменные X и Y переданные предикату father не связаны при его вызове.

clauses
    run():-
        console::init(),
        stdIO::write("Load  data\n"),
        reconsult("fa.txt"),
        stdIO::write("\nfather test\n"),
        father(X,Y),
        stdIO::writef("% is the father of %\n", Y, X),
        fail.

Заключение

На этом материале мы познакомились с тем, что программы, написанные в системе программирования Visual Prolog, часто очень похожи на программы, написанные в традиционном Прологе. Некоторый набор ключевых слов используется для обозначения различий частей исходного кода на языке системы Visual Prolog. Хотя Visual Prolog является объектно-ориентированным языком, сохраняется возможность разработки кода, не использующего свойства объектной ориентированности. Было показано работающее приложение консольного типа, которое поясняет как создавать подобные программы.

Мы увидели, кроме того, как эмулируется работа с динамическими данными, характерная для традиционного Пролога. Это делается путем помещения части кода в виде внешнего файла, отделенного от программы, представленной в двоичном формате. Синтаксис данных такого рода полностью соответствует синтаксису Пролога.

Ссылки