<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://wikiru.visual-prolog.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SergeMukhin</id>
	<title>wikiru.visual-prolog.com - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://wikiru.visual-prolog.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SergeMukhin"/>
	<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/SergeMukhin"/>
	<updated>2026-05-25T04:00:41Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2424</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2424"/>
		<updated>2010-03-26T13:14:25Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Other language features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic интерфейсы и классы ===&lt;br /&gt;
&lt;br /&gt;
смотри [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Generic_Interfaces_and_Classes Generic интерфейсы и классы (en)].&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Мониторы ===&lt;br /&gt;
&lt;br /&gt;
[http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors Мониторы (en)] с [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors#Guards гардами (en)]&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#inline inline (en)]: встроенные структуры и строки. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#union union (en)]: безфункторное объединение. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#byVal byVal (en)]: передача параметра по значению. &lt;br /&gt;
&lt;br /&gt;
По умолчанию, для соглашения о вызове &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt;, применяется внешнее разрешение.&lt;br /&gt;
* Они не могут иметь clauses&lt;br /&gt;
* Явное внешнее разрешение правильно только с динамической DLL&lt;br /&gt;
* Является процедурой&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* Приоритет унарного минуса изменен (теперь операция возведения имеет наивысший приоритет)&lt;br /&gt;
* Новые версии встроенных предикатов &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; м &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (добавлен первый параметр - тип).&lt;br /&gt;
* Новый встроенный предикат &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Новые аттрибуты:&lt;br /&gt;
* retired&lt;br /&gt;
* noDefaultConstructor&lt;br /&gt;
* used&lt;br /&gt;
&lt;br /&gt;
Новая или улучшенная диагностика:&lt;br /&gt;
* Локальный объектный предикат не использует &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (и может быть объявлен как классный)&lt;br /&gt;
* не используемая локальная константа&lt;br /&gt;
* Условие в &amp;lt;vp&amp;gt;foreach&amp;lt;/vp&amp;gt; не имеет точек бактракинга (должно быть или &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; или &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* Конверсия &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt; не верна для 64 bit платформ (например &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Проектное окно ===&lt;br /&gt;
&lt;br /&gt;
Проектное окно переписано заново. Функциональность практически не изменилась, увеличилась скорость и удобство.&lt;br /&gt;
&lt;br /&gt;
Окна &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; и &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; перенесены в проектное окно в правую панель предоосмотра.&lt;br /&gt;
&lt;br /&gt;
Появилась возможность создания в проекте несколько одноименных пакетов (для поддержки разных namespace например).&lt;br /&gt;
&lt;br /&gt;
=== Диалог просмотра===&lt;br /&gt;
&lt;br /&gt;
Диалог &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; ускорен и улучшен:&lt;br /&gt;
&lt;br /&gt;
=== Поиск в файлах===&lt;br /&gt;
&lt;br /&gt;
Поиск в файлах (&amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039;) ускорен и улучшен:&lt;br /&gt;
* окно результата может переиспользоваться&lt;br /&gt;
* Кнопки F8 и Shift+F8 служат для навигации&lt;br /&gt;
* добавлен режим &amp;quot;Prolog case sensitive&amp;quot;&lt;br /&gt;
* Запоминается последнее состояние&lt;br /&gt;
&lt;br /&gt;
=== поддержка Namespace ===&lt;br /&gt;
&lt;br /&gt;
Поддержка &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039; улучшена.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
Свойство &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; улучшено.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
Свойство &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) улучшено.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2423</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2423"/>
		<updated>2010-03-26T13:14:07Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Проектное окно */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic интерфейсы и классы ===&lt;br /&gt;
&lt;br /&gt;
смотри [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Generic_Interfaces_and_Classes Generic интерфейсы и классы (en)].&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Мониторы ===&lt;br /&gt;
&lt;br /&gt;
[http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors Мониторы (en)] с [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors#Guards гардами (en)]&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#inline inline (en)]: встроенные структуры и строки. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#union union (en)]: безфункторное объединение. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#byVal byVal (en)]: передача параметра по значению. &lt;br /&gt;
&lt;br /&gt;
По умолчанию, для соглашения о вызове &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt;, применяется внешнее разрешение.&lt;br /&gt;
* Они не могут иметь clauses&lt;br /&gt;
* Явное внешнее разрешение правильно только с динамической DLL&lt;br /&gt;
* Является процедурой&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* Приоритет унарного минуса изменен (теперь операция возведения имеет наивысший приоритет)&lt;br /&gt;
* Новые версии встроенных предикатов &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; м &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (добавлен первый параметр - тип).&lt;br /&gt;
* Новый встроенный предикат &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Удалено отличие между приватными и публичными интерфейсами в суппорт&lt;br /&gt;
&lt;br /&gt;
Новые аттрибуты:&lt;br /&gt;
* retired&lt;br /&gt;
* noDefaultConstructor&lt;br /&gt;
* used&lt;br /&gt;
&lt;br /&gt;
Новая или улучшенная диагностика:&lt;br /&gt;
* Локальный объектный предикат не использует &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (и может быть объявлен как классный)&lt;br /&gt;
* не используемая локальная константа&lt;br /&gt;
* Условие в &amp;lt;vp&amp;gt;foreach&amp;lt;/vp&amp;gt; не имеет точек бактракинга (должно быть или &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; или &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* Конверсия &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt; не верна для 64 bit платформ (например &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Проектное окно ===&lt;br /&gt;
&lt;br /&gt;
Проектное окно переписано заново. Функциональность практически не изменилась, увеличилась скорость и удобство.&lt;br /&gt;
&lt;br /&gt;
Окна &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; и &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; перенесены в проектное окно в правую панель предоосмотра.&lt;br /&gt;
&lt;br /&gt;
Появилась возможность создания в проекте несколько одноименных пакетов (для поддержки разных namespace например).&lt;br /&gt;
&lt;br /&gt;
=== Диалог просмотра===&lt;br /&gt;
&lt;br /&gt;
Диалог &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; ускорен и улучшен:&lt;br /&gt;
&lt;br /&gt;
=== Поиск в файлах===&lt;br /&gt;
&lt;br /&gt;
Поиск в файлах (&amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039;) ускорен и улучшен:&lt;br /&gt;
* окно результата может переиспользоваться&lt;br /&gt;
* Кнопки F8 и Shift+F8 служат для навигации&lt;br /&gt;
* добавлен режим &amp;quot;Prolog case sensitive&amp;quot;&lt;br /&gt;
* Запоминается последнее состояние&lt;br /&gt;
&lt;br /&gt;
=== поддержка Namespace ===&lt;br /&gt;
&lt;br /&gt;
Поддержка &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039; улучшена.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
Свойство &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; улучшено.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
Свойство &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) улучшено.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2422</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2422"/>
		<updated>2010-03-26T13:13:46Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* IDE */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic интерфейсы и классы ===&lt;br /&gt;
&lt;br /&gt;
смотри [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Generic_Interfaces_and_Classes Generic интерфейсы и классы (en)].&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Мониторы ===&lt;br /&gt;
&lt;br /&gt;
[http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors Мониторы (en)] с [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors#Guards гардами (en)]&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#inline inline (en)]: встроенные структуры и строки. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#union union (en)]: безфункторное объединение. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#byVal byVal (en)]: передача параметра по значению. &lt;br /&gt;
&lt;br /&gt;
По умолчанию, для соглашения о вызове &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt;, применяется внешнее разрешение.&lt;br /&gt;
* Они не могут иметь clauses&lt;br /&gt;
* Явное внешнее разрешение правильно только с динамической DLL&lt;br /&gt;
* Является процедурой&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* Приоритет унарного минуса изменен (теперь операция возведения имеет наивысший приоритет)&lt;br /&gt;
* Новые версии встроенных предикатов &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; м &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (добавлен первый параметр - тип).&lt;br /&gt;
* Новый встроенный предикат &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Удалено отличие между приватными и публичными интерфейсами в суппорт&lt;br /&gt;
&lt;br /&gt;
Новые аттрибуты:&lt;br /&gt;
* retired&lt;br /&gt;
* noDefaultConstructor&lt;br /&gt;
* used&lt;br /&gt;
&lt;br /&gt;
Новая или улучшенная диагностика:&lt;br /&gt;
* Локальный объектный предикат не использует &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (и может быть объявлен как классный)&lt;br /&gt;
* не используемая локальная константа&lt;br /&gt;
* Условие в &amp;lt;vp&amp;gt;foreach&amp;lt;/vp&amp;gt; не имеет точек бактракинга (должно быть или &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; или &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* Конверсия &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt; не верна для 64 bit платформ (например &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Проектное окно ===&lt;br /&gt;
&lt;br /&gt;
Проектное окно переписано заново. Функциональность практически не изменилась, увеличилась скорость и удобство.&lt;br /&gt;
&lt;br /&gt;
Окна &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; и &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; перенесены в проектное окно в правую панель предоосмотра.&lt;br /&gt;
&lt;br /&gt;
Появилась возможность создания в проекте несколько одноименных пакетов (для поддержки разных namesapce например).&lt;br /&gt;
&lt;br /&gt;
=== Диалог просмотра===&lt;br /&gt;
&lt;br /&gt;
Диалог &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; ускорен и улучшен:&lt;br /&gt;
&lt;br /&gt;
=== Поиск в файлах===&lt;br /&gt;
&lt;br /&gt;
Поиск в файлах (&amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039;) ускорен и улучшен:&lt;br /&gt;
* окно результата может переиспользоваться&lt;br /&gt;
* Кнопки F8 и Shift+F8 служат для навигации&lt;br /&gt;
* добавлен режим &amp;quot;Prolog case sensitive&amp;quot;&lt;br /&gt;
* Запоминается последнее состояние&lt;br /&gt;
&lt;br /&gt;
=== поддержка Namespace ===&lt;br /&gt;
&lt;br /&gt;
Поддержка &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039; улучшена.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
Свойство &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; улучшено.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
Свойство &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) улучшено.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2421</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2421"/>
		<updated>2010-03-26T13:06:27Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Other language features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic интерфейсы и классы ===&lt;br /&gt;
&lt;br /&gt;
смотри [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Generic_Interfaces_and_Classes Generic интерфейсы и классы (en)].&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Мониторы ===&lt;br /&gt;
&lt;br /&gt;
[http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors Мониторы (en)] с [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors#Guards гардами (en)]&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#inline inline (en)]: встроенные структуры и строки. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#union union (en)]: безфункторное объединение. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#byVal byVal (en)]: передача параметра по значению. &lt;br /&gt;
&lt;br /&gt;
По умолчанию, для соглашения о вызове &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt;, применяется внешнее разрешение.&lt;br /&gt;
* Они не могут иметь clauses&lt;br /&gt;
* Явное внешнее разрешение правильно только с динамической DLL&lt;br /&gt;
* Является процедурой&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* Приоритет унарного минуса изменен (теперь операция возведения имеет наивысший приоритет)&lt;br /&gt;
* Новые версии встроенных предикатов &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; м &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (добавлен первый параметр - тип).&lt;br /&gt;
* Новый встроенный предикат &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Удалено отличие между приватными и публичными интерфейсами в суппорт&lt;br /&gt;
&lt;br /&gt;
Новые аттрибуты:&lt;br /&gt;
* retired&lt;br /&gt;
* noDefaultConstructor&lt;br /&gt;
* used&lt;br /&gt;
&lt;br /&gt;
Новая или улучшенная диагностика:&lt;br /&gt;
* Локальный объектный предикат не использует &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (и может быть объявлен как классный)&lt;br /&gt;
* не используемая локальная константа&lt;br /&gt;
* Условие в &amp;lt;vp&amp;gt;foreach&amp;lt;/vp&amp;gt; не имеет точек бактракинга (должно быть или &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; или &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* Конверсия &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt; не верна для 64 bit платформ (например &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2420</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2420"/>
		<updated>2010-03-26T07:27:15Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Other language features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic интерфейсы и классы ===&lt;br /&gt;
&lt;br /&gt;
смотри [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Generic_Interfaces_and_Classes Generic интерфейсы и классы (en)].&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Мониторы ===&lt;br /&gt;
&lt;br /&gt;
[http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors Мониторы (en)] с [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors#Guards гардами (en)]&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#inline inline (en)]: встроенные структуры и строки. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#union union (en)]: безфункторное объединение. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#byVal byVal (en)]: передача параметра по значению. &lt;br /&gt;
&lt;br /&gt;
По умолчанию, для соглашения о вызове &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt;, применяется внешнее разрешение.&lt;br /&gt;
* Они не могут иметь clauses&lt;br /&gt;
* Явное внешнее разрешение правильно только с динамической DLL&lt;br /&gt;
* Является процедурой&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* Приоритет унарного минуса изменен (теперь операция возведения имеет наивысший приоритет)&lt;br /&gt;
* Новые версии встроенных предикатов &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; м &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (добавлен первый параметр - тип).&lt;br /&gt;
* Новый встроенный предикат &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
Новые аттрибуты:&lt;br /&gt;
* retired&lt;br /&gt;
* noDefaultConstructor&lt;br /&gt;
* used&lt;br /&gt;
&lt;br /&gt;
Новая или улучшенная диагностика:&lt;br /&gt;
* Локальный объектный предикат не использует &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (и может быть объявлен как классный)&lt;br /&gt;
* не используемая локальная константа&lt;br /&gt;
* Условие в &amp;lt;vp&amp;gt;foreach&amp;lt;/vp&amp;gt; не имеет точек бактракинга (должно быть или &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; или &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* Конверсия &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt; не верна для 64 bit платформ (например &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2419</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2419"/>
		<updated>2010-03-26T07:20:43Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Интерфейс с другими языками */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic интерфейсы и классы ===&lt;br /&gt;
&lt;br /&gt;
смотри [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Generic_Interfaces_and_Classes Generic интерфейсы и классы (en)].&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Мониторы ===&lt;br /&gt;
&lt;br /&gt;
[http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors Мониторы (en)] с [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors#Guards гардами (en)]&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#inline inline (en)]: встроенные структуры и строки. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#union union (en)]: безфункторное объединение. &lt;br /&gt;
* [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Attributes#byVal byVal (en)]: передача параметра по значению. &lt;br /&gt;
&lt;br /&gt;
По умолчанию, для соглашения о вызове &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt;, применяется внешнее разрешение.&lt;br /&gt;
* Они не могут иметь clauses&lt;br /&gt;
* Явное внешнее разрешение правильно только с динамической DLL&lt;br /&gt;
* Является процедурой&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2418</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2418"/>
		<updated>2010-03-26T07:15:33Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Язык */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic интерфейсы и классы ===&lt;br /&gt;
&lt;br /&gt;
смотри [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Generic_Interfaces_and_Classes Generic интерфейсы и классы (en)].&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Мониторы ===&lt;br /&gt;
&lt;br /&gt;
[http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors Мониторы (en)] с [http://wiki.visual-prolog.com/index.php?title=Language_Reference/Monitors#Guards гардами (en)]&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2417</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2417"/>
		<updated>2010-03-25T15:40:47Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Debugger */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* Измененные переменные и их значения показываются выделением &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Улучшен просмотр длинных списков&lt;br /&gt;
* Ускорен рестарт отладчика&lt;br /&gt;
* Появились точки останова (&amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039;) по памяти и для некоторых типов фактов&lt;br /&gt;
* Улучшена работа с несколькими thread (добавлена возможность указания имени thread, улучшена обработка прерываний в thread)&lt;br /&gt;
* При длинных значениях tooltip может показываеться в несколько строк&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2416</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2416"/>
		<updated>2010-03-25T15:36:22Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Others */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* Более эффективное &amp;#039;&amp;#039;&amp;#039;управление памятью&amp;#039;&amp;#039;&amp;#039;; во многих случаях используется типовая аллокация памяти, для составных доменов, списков и цепочек баз данных&lt;br /&gt;
* Несколько различных оптимизаций для увеличения скорости и уменьшения генерируемого кода&lt;br /&gt;
* Новые примеры для демонстрации (Demo Examples) (только для коммерческой версии):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: новая опция для игнорирования директивы &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt;&lt;br /&gt;
* В библиотеку &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; добавлены имена из многих MS библиотек.&lt;br /&gt;
* При ошибки выполнения предиката consult выдается добавочная информация, позволяющая локализовать ошибку в базе данных&lt;br /&gt;
* Ускорен линкер&lt;br /&gt;
* Интеграция со службой Vault теперь работает с версией 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2415</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2415"/>
		<updated>2010-03-25T15:29:20Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* New entities */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== Новые пакеты ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* More efficient &amp;#039;&amp;#039;&amp;#039;memory handling&amp;#039;&amp;#039;&amp;#039;; using typed memory allocation for compound terms and internal facts chains&lt;br /&gt;
* Various optimizations for speed and size of generated code&lt;br /&gt;
* New Demo Examples (Commercial Edition only):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: extra option to ignore &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt; directives&lt;br /&gt;
* Extend &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; library (with more names from MS libraries).&lt;br /&gt;
* More context to consult exceptions&lt;br /&gt;
* Linker speed improvement&lt;br /&gt;
* Vault Integration updated to version 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2414</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2414"/>
		<updated>2010-03-25T15:28:41Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Sorting in various windows */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Сортировка в некоторых окнах ===&lt;br /&gt;
&lt;br /&gt;
Окно ошибок (Errors Window), точек останова (Break points Window) и некоторые другие появилась возможность сортировать колонки. Для этого надо просто нажать на вверх колонки.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== New entities ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* More efficient &amp;#039;&amp;#039;&amp;#039;memory handling&amp;#039;&amp;#039;&amp;#039;; using typed memory allocation for compound terms and internal facts chains&lt;br /&gt;
* Various optimizations for speed and size of generated code&lt;br /&gt;
* New Demo Examples (Commercial Edition only):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: extra option to ignore &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt; directives&lt;br /&gt;
* Extend &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; library (with more names from MS libraries).&lt;br /&gt;
* More context to consult exceptions&lt;br /&gt;
* Linker speed improvement&lt;br /&gt;
* Vault Integration updated to version 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2413</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2413"/>
		<updated>2010-03-25T15:26:33Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Tab navigation dialog */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation диалог (Ctrl+Tab) ===&lt;br /&gt;
&lt;br /&gt;
Добавлена функциональность &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; диалога:&lt;br /&gt;
* при использовании кнопки ALT фильтруются все [ReadOnly] окна&lt;br /&gt;
* при нажатии Del - соответствующее окно закрывается&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Sorting in various windows ===&lt;br /&gt;
&lt;br /&gt;
The Errors Window, the Break points Window, etc. has been extended with sorting capabilities (clicking on the top banner).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== New entities ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* More efficient &amp;#039;&amp;#039;&amp;#039;memory handling&amp;#039;&amp;#039;&amp;#039;; using typed memory allocation for compound terms and internal facts chains&lt;br /&gt;
* Various optimizations for speed and size of generated code&lt;br /&gt;
* New Demo Examples (Commercial Edition only):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: extra option to ignore &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt; directives&lt;br /&gt;
* Extend &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; library (with more names from MS libraries).&lt;br /&gt;
* More context to consult exceptions&lt;br /&gt;
* Linker speed improvement&lt;br /&gt;
* Vault Integration updated to version 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2412</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2412"/>
		<updated>2010-03-25T12:55:42Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Язык ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Подготовка к 64-битной реализации 64bit systems: built-in типы &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; и &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Интерфейс с другими языками ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; dialog functionality has been extended:&lt;br /&gt;
* use ALT button for filtering [ReadOnly] windows&lt;br /&gt;
* use Del for closing windows&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Sorting in various windows ===&lt;br /&gt;
&lt;br /&gt;
The Errors Window, the Break points Window, etc. has been extended with sorting capabilities (clicking on the top banner).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== New entities ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* More efficient &amp;#039;&amp;#039;&amp;#039;memory handling&amp;#039;&amp;#039;&amp;#039;; using typed memory allocation for compound terms and internal facts chains&lt;br /&gt;
* Various optimizations for speed and size of generated code&lt;br /&gt;
* New Demo Examples (Commercial Edition only):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: extra option to ignore &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt; directives&lt;br /&gt;
* Extend &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; library (with more names from MS libraries).&lt;br /&gt;
* More context to consult exceptions&lt;br /&gt;
* Linker speed improvement&lt;br /&gt;
* Vault Integration updated to version 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2411</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2411"/>
		<updated>2010-03-25T12:53:50Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Термы универсального типа */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Preparations for supporting 64bit systems: built-in types &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Foreign language interfacing ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; dialog functionality has been extended:&lt;br /&gt;
* use ALT button for filtering [ReadOnly] windows&lt;br /&gt;
* use Del for closing windows&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Sorting in various windows ===&lt;br /&gt;
&lt;br /&gt;
The Errors Window, the Break points Window, etc. has been extended with sorting capabilities (clicking on the top banner).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== New entities ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* More efficient &amp;#039;&amp;#039;&amp;#039;memory handling&amp;#039;&amp;#039;&amp;#039;; using typed memory allocation for compound terms and internal facts chains&lt;br /&gt;
* Various optimizations for speed and size of generated code&lt;br /&gt;
* New Demo Examples (Commercial Edition only):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: extra option to ignore &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt; directives&lt;br /&gt;
* Extend &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; library (with more names from MS libraries).&lt;br /&gt;
* More context to consult exceptions&lt;br /&gt;
* Linker speed improvement&lt;br /&gt;
* Vault Integration updated to version 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2410</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2410"/>
		<updated>2010-03-25T12:53:29Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify оператор ===&lt;br /&gt;
&lt;br /&gt;
Новый оператор == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Термы универсального типа  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; и предикат&amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подготовка к 64-битной реализации ===&lt;br /&gt;
&lt;br /&gt;
Preparations for supporting 64bit systems: built-in types &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Foreign language interfacing ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; dialog functionality has been extended:&lt;br /&gt;
* use ALT button for filtering [ReadOnly] windows&lt;br /&gt;
* use Del for closing windows&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Sorting in various windows ===&lt;br /&gt;
&lt;br /&gt;
The Errors Window, the Break points Window, etc. has been extended with sorting capabilities (clicking on the top banner).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== New entities ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* More efficient &amp;#039;&amp;#039;&amp;#039;memory handling&amp;#039;&amp;#039;&amp;#039;; using typed memory allocation for compound terms and internal facts chains&lt;br /&gt;
* Various optimizations for speed and size of generated code&lt;br /&gt;
* New Demo Examples (Commercial Edition only):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: extra option to ignore &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt; directives&lt;br /&gt;
* Extend &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; library (with more names from MS libraries).&lt;br /&gt;
* More context to consult exceptions&lt;br /&gt;
* Linker speed improvement&lt;br /&gt;
* Vault Integration updated to version 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2409</id>
		<title>Что нового в VIP 7.3</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A7%D1%82%D0%BE_%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%B2_VIP_7.3&amp;diff=2409"/>
		<updated>2010-03-25T12:51:07Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: Новая: {{Preliminary Documentation}}  == Language ==  === Generic Interfaces and Classes ===  See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.  === Conversion to Gene...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Preliminary Documentation}}&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
=== Generic Interfaces and Classes ===&lt;br /&gt;
&lt;br /&gt;
See {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.&lt;br /&gt;
&lt;br /&gt;
=== Conversion to Generic Type ===&lt;br /&gt;
&lt;br /&gt;
=== Monitors ===&lt;br /&gt;
&lt;br /&gt;
{{lang|Monitors|Monitors}} with {{lang2|Monitors|Guards|guards}}&lt;br /&gt;
&lt;br /&gt;
=== Must Unify Operator ===&lt;br /&gt;
&lt;br /&gt;
New operator == (must-unify)&lt;br /&gt;
&lt;br /&gt;
=== Universal term type  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;any&amp;lt;/vp&amp;gt; and the predicate &amp;lt;vp&amp;gt;toAny/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 64 bit preparation ===&lt;br /&gt;
&lt;br /&gt;
Preparations for supporting 64bit systems: built-in types &amp;lt;vp&amp;gt;integerNative&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;unsignedNative&amp;lt;/vp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Foreign language interfacing ===&lt;br /&gt;
&lt;br /&gt;
Native interfaces support (attributes):&lt;br /&gt;
* {{lang2|Attributes|inline|inline}}: Inline structures and strings. &lt;br /&gt;
* {{lang2|Attributes|union|union}}: Functor-less unions. &lt;br /&gt;
* {{lang2|Attributes|byVal|byVal}}: Passing parameters by value. &lt;br /&gt;
&lt;br /&gt;
External resolution is default for predicates declared with &amp;lt;vp&amp;gt;apicall&amp;lt;/vp&amp;gt; calling convention.&lt;br /&gt;
* They cannot have clauses&lt;br /&gt;
* explicit externally resolution is only legal when a DLL is stated&lt;br /&gt;
&lt;br /&gt;
=== Other language features ===&lt;br /&gt;
&lt;br /&gt;
* The precedence of unary minus is changed (so that power operator has higher precedence)&lt;br /&gt;
* Extended versions of built-in predicates &amp;lt;vp&amp;gt;toTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;tryToTerm/2-&amp;gt;&amp;lt;/vp&amp;gt; (type as the first parameter).&lt;br /&gt;
* New built-in predicate &amp;lt;vp&amp;gt;fromEllipsis : (...) -&amp;gt; any* Terms&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Runtime distinction between privately and publicly supported interfaces is removed&lt;br /&gt;
&lt;br /&gt;
New attributes&lt;br /&gt;
* {{lang2|Attributes|retired|retired}}&lt;br /&gt;
* {{lang2|Attributes|noDefaultConstructor|noDefaultConstructor}}&lt;br /&gt;
* {{lang2|Attributes|used|used}}&lt;br /&gt;
&lt;br /&gt;
Warnings:&lt;br /&gt;
* Local object predicates which do not use &amp;lt;vp&amp;gt;This&amp;lt;/vp&amp;gt; (and therefore can be declared as class predicates)&lt;br /&gt;
* Unused local constants&lt;br /&gt;
* Condition of foreach statement which has no backtrack point (i.e. mode is not &amp;lt;vp&amp;gt;multi&amp;lt;/vp&amp;gt; or &amp;lt;vp&amp;gt;nondeterm&amp;lt;/vp&amp;gt;)&lt;br /&gt;
* &amp;lt;vp&amp;gt;unheckedConversion&amp;lt;/vp&amp;gt;&amp;#039;s that would be illegal on 64 bit platforms (e.g. &amp;lt;vp&amp;gt;pointer&amp;lt;/vp&amp;gt; -&amp;gt; &amp;lt;vp&amp;gt;integer&amp;lt;/vp&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== IDE ==&lt;br /&gt;
&lt;br /&gt;
=== Project tree ===&lt;br /&gt;
&lt;br /&gt;
The project tree is redesigned and reimplemented.  The functionality is more or less unchanged, but the performance is improved.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IncludedIn&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Includes&amp;#039;&amp;#039;&amp;#039; windows has been moved to preview pane of project window.&lt;br /&gt;
&lt;br /&gt;
It is not possible to have several packages with same name in a project (which can make much sense when using namespaces).&lt;br /&gt;
&lt;br /&gt;
=== Browse dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Browse&amp;#039;&amp;#039;&amp;#039; dialog is improved in several smaller respects, including:&lt;br /&gt;
* It automatically jump to the 1st occurrence of search entity on locate&lt;br /&gt;
* The last dialog position is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Find In Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Find In Files&amp;#039;&amp;#039;&amp;#039; dialog is improved in several respects, including:&lt;br /&gt;
* result window is reused for subsequent searches&lt;br /&gt;
* F8 button for next match (Shift+F8 - previous)&lt;br /&gt;
* Prolog case sensitive search mode&lt;br /&gt;
* state is saved for next appearence&lt;br /&gt;
&lt;br /&gt;
=== Namespace support ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Namespaces support&amp;#039;&amp;#039;&amp;#039; is improved, so that forms, dialogs, etc can be places in namespaces when created.&lt;br /&gt;
&lt;br /&gt;
=== IntelliSense ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;IntelliSense&amp;#039;&amp;#039;&amp;#039; feature is improved for better overview, speed typing and convenience of work.&lt;br /&gt;
&lt;br /&gt;
=== Tab navigation dialog ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;tab navigation&amp;#039;&amp;#039;&amp;#039; dialog functionality has been extended:&lt;br /&gt;
* use ALT button for filtering [ReadOnly] windows&lt;br /&gt;
* use Del for closing windows&lt;br /&gt;
&lt;br /&gt;
=== Go to Position on Clipboard ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Go to Position on Clipboard&amp;#039;&amp;#039;&amp;#039; (Shift+F2) has been extended to accept a complete exception dump.  F8 will go to the next stack entry.&lt;br /&gt;
&lt;br /&gt;
=== Sorting in various windows ===&lt;br /&gt;
&lt;br /&gt;
The Errors Window, the Break points Window, etc. has been extended with sorting capabilities (clicking on the top banner).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Optimal Set of Includes improved&amp;#039;&amp;#039;&amp;#039; (output, local scopes, etc.)&lt;br /&gt;
&lt;br /&gt;
== Debugger ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Highlighting&amp;#039;&amp;#039;&amp;#039; changed values in variable window&lt;br /&gt;
* View of &amp;#039;&amp;#039;&amp;#039;long lists&amp;#039;&amp;#039;&amp;#039; improvement&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; of restarting is improved&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory break points&amp;#039;&amp;#039;&amp;#039; and fact access (for some types)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multi-threaded&amp;#039;&amp;#039;&amp;#039; application debugging is improved (thread names, break points handling)&lt;br /&gt;
* Multi-line &amp;#039;&amp;#039;&amp;#039;tool tips&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== PFC ==&lt;br /&gt;
&lt;br /&gt;
=== New entities ===&lt;br /&gt;
&lt;br /&gt;
* [[Collection library]]&lt;br /&gt;
** Algebraic: &amp;lt;vp&amp;gt;redBlackSet&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;leftistPriorityQueue&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Modifiable: &amp;lt;vp&amp;gt;mapM_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueM_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueM_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setM_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
** Persistent: &amp;lt;vp&amp;gt;mapP_redBlack&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;priorityQueueP_leftist&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;queueP_fact&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;setP_redBlack&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;varM&amp;lt;/vp&amp;gt; modifiable variable&lt;br /&gt;
* &amp;lt;vp&amp;gt;linkControl&amp;lt;/vp&amp;gt; PFC version of the Link common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;richEditControl&amp;lt;/vp&amp;gt; PFC version of the RichEdit common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;treeControl&amp;lt;/vp&amp;gt; PFC model based version of the TreeView common control&lt;br /&gt;
* &amp;lt;vp&amp;gt;gdiplus&amp;lt;/vp&amp;gt; PFC version of GDI+&lt;br /&gt;
* &amp;lt;vp&amp;gt;cryptography&amp;lt;/vp&amp;gt; hash, sha1, md5 &amp;amp; base64&lt;br /&gt;
* &amp;lt;vp&amp;gt;eventSource&amp;lt;/vp&amp;gt; generalization of event notification/listening&lt;br /&gt;
* &amp;lt;vp&amp;gt;monitorQueue&amp;lt;/vp&amp;gt; thread safe queue class based on the monitor facility&lt;br /&gt;
* &amp;lt;vp&amp;gt;reflection&amp;lt;/vp&amp;gt; basic functionalty for code reflection&lt;br /&gt;
* &amp;lt;vp&amp;gt;inputStream_null&amp;lt;/vp&amp;gt; &amp;amp; &amp;lt;vp&amp;gt;outputStream_null&amp;lt;/vp&amp;gt; media-less streams (input is exhausted; outpt throws away)&lt;br /&gt;
* &amp;lt;vp&amp;gt;lZ_fileSystem_native&amp;lt;/vp&amp;gt; Interface to Lempel-Ziv Decompression API functionality&lt;br /&gt;
* &amp;lt;vp&amp;gt;shell_api&amp;lt;/vp&amp;gt; Api level interface to the Windows Shell&lt;br /&gt;
* &amp;lt;vp&amp;gt;winsock2_native&amp;lt;/vp&amp;gt; native bindings to Windows Sockets 2&lt;br /&gt;
&lt;br /&gt;
=== Extensions and improvements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;list&amp;lt;/vp&amp;gt; package:&lt;br /&gt;
** speed: &amp;lt;vp&amp;gt;sort&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;removeDuplicate&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;drop&amp;lt;/vp&amp;gt;, &amp;lt;vp&amp;gt;min&amp;lt;/vp&amp;gt;/&amp;lt;vp&amp;gt;max&amp;lt;/vp&amp;gt;, etc.)&lt;br /&gt;
** functionality: &amp;lt;vp&amp;gt;isMemberEq&amp;lt;/vp&amp;gt; (and similar predicates) that uses a &amp;lt;vp&amp;gt;determ&amp;lt;/vp&amp;gt; predicate as test&lt;br /&gt;
* &amp;lt;vp&amp;gt;listControl&amp;lt;/vp&amp;gt; with owner-drawing capabilities&lt;br /&gt;
* &amp;lt;vp&amp;gt;uxTheme_native&amp;lt;/vp&amp;gt; extended with the rest of the functions and the constants from vsStyle.h, etc.&lt;br /&gt;
* Add moving listener/responder to &amp;lt;vp&amp;gt;splitTwoControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Add the fraction handling from &amp;lt;vp&amp;gt;format&amp;lt;/vp&amp;gt; to &amp;lt;vp&amp;gt;formatTime&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039; improvements for: &lt;br /&gt;
** &amp;lt;vp&amp;gt;string&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;fileName&amp;lt;/vp&amp;gt;&lt;br /&gt;
** &amp;lt;vp&amp;gt;listViewControl&amp;lt;/vp&amp;gt;&lt;br /&gt;
* &amp;lt;vp&amp;gt;string::rear/2-&amp;gt;&amp;lt;/vp&amp;gt; returns the rear part of a string&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Math&amp;#039;&amp;#039;&amp;#039; package: predicates &amp;lt;vp&amp;gt;roundToInteger64/1-&amp;gt;&amp;lt;/vp&amp;gt; and &amp;lt;vp&amp;gt;roundToUnsigned64/1-&amp;gt;&amp;lt;/vp&amp;gt;&lt;br /&gt;
* Better handling of &amp;#039;&amp;#039;&amp;#039;default button size&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;lt;vp&amp;gt;msXLM_api&amp;lt;/vp&amp;gt; update to version 6.0 of various COMponent classes&lt;br /&gt;
&lt;br /&gt;
== Others ==&lt;br /&gt;
&lt;br /&gt;
* More efficient &amp;#039;&amp;#039;&amp;#039;memory handling&amp;#039;&amp;#039;&amp;#039;; using typed memory allocation for compound terms and internal facts chains&lt;br /&gt;
* Various optimizations for speed and size of generated code&lt;br /&gt;
* New Demo Examples (Commercial Edition only):&lt;br /&gt;
** Parser Generator&lt;br /&gt;
** LZDecompression&lt;br /&gt;
** TreeControlDemo&lt;br /&gt;
* Help on built-in entities&lt;br /&gt;
* VipBuilder: extra option to ignore &amp;lt;vp&amp;gt;#requires&amp;lt;/vp&amp;gt; directives&lt;br /&gt;
* Extend &amp;#039;&amp;#039;&amp;#039;Win32&amp;#039;&amp;#039;&amp;#039; library (with more names from MS libraries).&lt;br /&gt;
* More context to consult exceptions&lt;br /&gt;
* Linker speed improvement&lt;br /&gt;
* Vault Integration updated to version 5.0.1&lt;br /&gt;
&lt;br /&gt;
[[Category:Release Notes]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D1%8B._%D0%A1%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%B8_%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=2188</id>
		<title>Классы. Статические и динамические сущности</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D1%8B._%D0%A1%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%B8_%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=2188"/>
		<updated>2008-08-08T08:43:09Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Особенности использования фактов-переменных */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Классы==&lt;br /&gt;
Каждый класс должен иметь декларацию класса и имплементацию.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Декларация класса&amp;#039;&amp;#039;&amp;#039; содержит набор объявлений констант, доменов, предикатов, видимых извне.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Имплементация класса&amp;#039;&amp;#039;&amp;#039; содержит &lt;br /&gt;
*собственно реализацию предикатов (клаузы), объявленных в декларации,&lt;br /&gt;
*декларации констант,&lt;br /&gt;
*декларации и реализации предикатов, используемых в клаузах,&lt;br /&gt;
*декларации фактов. &lt;br /&gt;
*Факты и предикаты, объявленные в имплементации класса, извне не видны.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cтатический класс&amp;#039;&amp;#039;&amp;#039; - класс не способный порождать объекты (экземпляры или точные копии). &lt;br /&gt;
Все предикаты и разделы фактов статического класса существуют в единственном экземпляре во всем проекте. &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Динамический класс&amp;#039;&amp;#039;&amp;#039; - класс, способный порождать объекты (экземпляры или точные копии),&lt;br /&gt;
Динамические классы могут содержать как статические, так и динамические предикаты, а также статические и динамические факты. &lt;br /&gt;
Каждый экземпляр динамического класса содержит свою копию динамического предиката и свою копию динамического раздела фактов. &lt;br /&gt;
Создание объекта (экземпляра или копии класса) осуществляется вызовом предиката-конструктора. Как правило, это предикат-функция &amp;#039;&amp;#039;new()&amp;#039;&amp;#039;, но может быть и любой другой, объявленный как конструктор в разделе &amp;#039;&amp;#039;constructors&amp;#039;&amp;#039; декларации класса. &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Статический класс&amp;#039;&amp;#039;&amp;#039; - это класс, не имеющий интерфейса. &lt;br /&gt;
Или, что то же самое, - если класс не имеет инерфейса, то он - статический, &lt;br /&gt;
&lt;br /&gt;
Если класс имеет интерфейс - то он - динамический. &lt;br /&gt;
&lt;br /&gt;
==Статические классы==&lt;br /&gt;
Например, класс &amp;#039;&amp;#039;staticClass&amp;#039;&amp;#039; является статическим классом. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
class staticClass&lt;br /&gt;
end class staticClass&lt;br /&gt;
&lt;br /&gt;
implement staticClass&lt;br /&gt;
end implement staticClass&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
но этот класс ничего не способен делать. &lt;br /&gt;
Чтобы что-то делать, добавим в этот класс декларации предикатов и клаузы. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class staticClass&lt;br /&gt;
predicates&lt;br /&gt;
   visibleStaticPred:().&lt;br /&gt;
end class staticClass&lt;br /&gt;
&lt;br /&gt;
implement staticClass&lt;br /&gt;
clauses&lt;br /&gt;
     visibleStaticPred():-&lt;br /&gt;
       ...&lt;br /&gt;
       invisibleStaticPred(),&lt;br /&gt;
       ...         &lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
   invisibleStaticPred:().&lt;br /&gt;
clauses&lt;br /&gt;
   invisibleStaticPred():-&lt;br /&gt;
       ...&lt;br /&gt;
end implement staticClass&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Обращения к предикатам этого класса записываются как &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
staticClass::staticVisiblePred(),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
(существенно использования именно &amp;#039;&amp;#039;&amp;#039;::&amp;#039;&amp;#039;&amp;#039; для обращения к статической сущности) &lt;br /&gt;
&lt;br /&gt;
Здесь предикат &amp;#039;&amp;#039;invisibleStaticPred&amp;#039;&amp;#039; является предикатом, объявленным внутри имплементации, и только внутри этой имплементации может быть вызван. &lt;br /&gt;
То есть предикаты, объявленные в декларации класса, являются заведомо всегда статическими предикатами, а раздел объявлений предикатов внутри имплементации статического класса всегда должен имет вид class predicates &lt;br /&gt;
Если попробовать создать экземпляр класса &amp;#039;&amp;#039;staticClass&amp;#039;&amp;#039; с помощью вызова конструктора &amp;#039;&amp;#039;new()&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
staticClass::new(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
то это не пропустит компилятор. &lt;br /&gt;
Таким образом, статические классы - это просто модули проекта. &lt;br /&gt;
Проект, построенный только на модулях (статических классах) - не имеет никакого отношения к объекто-ориентированной концепции программирования. &lt;br /&gt;
&lt;br /&gt;
==Динамические классы==&lt;br /&gt;
Например, класс &amp;#039;&amp;#039;dynamicClass&amp;#039;&amp;#039; является динамическим классом, что определяется &lt;br /&gt;
конструкцией &amp;#039;&amp;#039;dynamicClass:justInterface&amp;#039;&amp;#039;. &lt;br /&gt;
&amp;lt;vip&amp;gt;class dynamicClass:justInterface&lt;br /&gt;
end class dynamicClass &lt;br /&gt;
&lt;br /&gt;
implement dynamicClass &lt;br /&gt;
end implement dynamicClass &lt;br /&gt;
&lt;br /&gt;
interface justInterface&lt;br /&gt;
end interface justInterface&amp;lt;/vip&amp;gt;&lt;br /&gt;
Опять-таки, синтаксически все правильно, но практически бесполезно. &lt;br /&gt;
Теперь добавим какие-нибудь полезности и вынесем вперед декларацию интерфейса. &lt;br /&gt;
&amp;lt;vip&amp;gt;interface justInterface&lt;br /&gt;
predicates&lt;br /&gt;
   visibleDynamicPred:().&lt;br /&gt;
end interface justInterface&lt;br /&gt;
&lt;br /&gt;
class dynamicClass:justInterface&lt;br /&gt;
predicates&lt;br /&gt;
   visibleStaticPred:().&lt;br /&gt;
end class dynamicClass &lt;br /&gt;
&lt;br /&gt;
implement dynamicClass &lt;br /&gt;
clauses&lt;br /&gt;
   visibleStaticPred():-&lt;br /&gt;
      ...&lt;br /&gt;
      invisibleStaticPred(),&lt;br /&gt;
      ...         &lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
   visibleDynamicPred():-&lt;br /&gt;
     ...&lt;br /&gt;
     invisibleStaticPred(),&lt;br /&gt;
     ...         &lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
   invisibleStaticPred:().&lt;br /&gt;
clauses&lt;br /&gt;
   invisibleStaticPred():-&lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
   invisibleDynamicPred:().&lt;br /&gt;
clauses&lt;br /&gt;
   invisibleDynamicPred():-&lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
end implement dynamicClass&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь мы имеем четыре предиката, которые имеют различные динамические особенности.&lt;br /&gt;
&amp;#039;&amp;#039;visibleDynamicPred&amp;#039;&amp;#039; - динамический предикат, принадлежащий интерфесу &amp;#039;&amp;#039;justInterface&amp;#039;&amp;#039;, видимый извне. &lt;br /&gt;
&amp;#039;&amp;#039;visibleStaticPred&amp;#039;&amp;#039; - статический предикат, видимый извне. &lt;br /&gt;
&amp;#039;&amp;#039;invisibleStaticPred&amp;#039;&amp;#039; - статический предикат, определенный в имплементации класса &amp;#039;&amp;#039;dynamicClass&amp;#039;&amp;#039;, невидимый извне. &lt;br /&gt;
&amp;#039;&amp;#039;invisibleDynamicPred&amp;#039;&amp;#039; - динамический предикат, определенный в имплементации класса &amp;#039;&amp;#039;dynamicClass&amp;#039;&amp;#039;, невидимый извне. &lt;br /&gt;
К такому классу можно обратиться как к статическому &lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
dynamicClass::visibleStaticPred(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
но нельзя так: &lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
dynamicClass::visibleDynamicPred(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Чтобы вызвать предикат &amp;#039;&amp;#039;visibleDynamicPred()&amp;#039;&amp;#039;, необходимо с помощью конструктора создать экземпляр класса и обратиться к этому экземпляру:&lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
ЭкземплярDynamicClass=dynamicClass::new(),&lt;br /&gt;
ЭкземплярDynamicClass:visibleDynamicPred(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Примечания: &lt;br /&gt;
* Конструктор &amp;#039;&amp;#039;new()&amp;#039;&amp;#039; является предопределенным конструктором, объявление которого не требуется, если в имплементации нет для него клауза; &lt;br /&gt;
* В обращении к экземпляру класса (объекту) используется разделитель &amp;#039;:&amp;#039; (а не &amp;#039;::&amp;#039;, как этого требует обращение к статической сущности).&lt;br /&gt;
&lt;br /&gt;
==Факты==&lt;br /&gt;
Факты декларируются и могут быть использованы в клаузах предикатов только в имплементации класса.&lt;br /&gt;
Нельзя объявить факты в одном классе и пытаться использовать обращение к ним в другом классе.&lt;br /&gt;
В статических классах факты должны быть объявлены только как статические.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;implement example&lt;br /&gt;
...&lt;br /&gt;
class facts&lt;br /&gt;
  myFact_F:(string,string).&lt;br /&gt;
...&lt;br /&gt;
end implement example&amp;lt;/vip&amp;gt;&lt;br /&gt;
В имплементациях динамических классов факты могут быть объявлены либо как статические, либо как динамические:&lt;br /&gt;
&amp;lt;vip&amp;gt;class example1:example1interface&lt;br /&gt;
predicates&lt;br /&gt;
  запомнитьГород:(string Город).&lt;br /&gt;
  запомнитьИмяАдрес:(string Имя, string Адрес).&lt;br /&gt;
  получитьГород:()-&amp;gt;string Город.&lt;br /&gt;
  получитьИмяАдрес:(string Имя, string Адрес).&lt;br /&gt;
end class example1&lt;br /&gt;
&lt;br /&gt;
implement example1&lt;br /&gt;
...&lt;br /&gt;
class facts&lt;br /&gt;
  город_V:string Город.&lt;br /&gt;
...&lt;br /&gt;
facts&lt;br /&gt;
  имяАдрес_F:(string Адрес, string Имя).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  запомнитьГород(Город):-&lt;br /&gt;
    город_V:=Город.&lt;br /&gt;
...&lt;br /&gt;
clauses&lt;br /&gt;
  запомнитьИмяАдрес(Имя,Адрес):-&lt;br /&gt;
    assert(имяАдрес_F:(Адрес,Имя)).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  получитьГород(город_V).&lt;br /&gt;
  ...&lt;br /&gt;
clauses&lt;br /&gt;
  получитьИмяАдрес(Имя,Адрес):-&lt;br /&gt;
    имяАдрес_F(Адрес,Имя),&lt;br /&gt;
    !,&lt;br /&gt;
    ...&lt;br /&gt;
end implement example1&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
В приведенном примере класса &amp;#039;&amp;#039;example1&amp;#039;&amp;#039; факт &amp;#039;&amp;#039;город_V&amp;#039;&amp;#039; является статическим, а факты &amp;#039;&amp;#039;имяАдрес_F(...)&amp;#039;&amp;#039; являются динамическими,&lt;br /&gt;
что определяется разницей в имени раздела &amp;#039;&amp;#039;class facts&amp;#039;&amp;#039; и просто &amp;#039;&amp;#039;facts&amp;#039;&amp;#039;.&lt;br /&gt;
Поскольку статическая сущность любого класса всего одна, а динамическая - по одной на каждый экземпляр - то и в нашем примере запрос &amp;#039;&amp;#039;получитьГород()&amp;#039;&amp;#039;, переданный любому экземпляру, вернет один и тот же результат, а запрос &amp;#039;&amp;#039;получитьИмяАдрес(...)&amp;#039;&amp;#039; вернет для каждого экземпляра свое значение.&lt;br /&gt;
===Особенности использования фактов-переменных===&lt;br /&gt;
В примере выше использовался факт-переменная &amp;#039;&amp;#039;город_V&amp;#039;&amp;#039;, особенностью которого является то, что такой факт всегда один и он сам несет значение терма, объявленного в декларации.&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
class facts&lt;br /&gt;
  город_V:string Город.&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь факт &amp;#039;&amp;#039;город_V&amp;#039;&amp;#039; имеет строковое значение. Возможны значения любых объявленных доменов, например&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  персона_V:мойДомен.&amp;lt;/vip&amp;gt;&lt;br /&gt;
В отличие от обычных фактов, факт-переменная всегда существует и должен иметь начальное значение.&lt;br /&gt;
Начальное значение факту-переменной можно задать при объявлении&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  персона_V:мойДомен:=персона(&amp;quot;Старик&amp;quot;,80).&lt;br /&gt;
  город_V:string:=&amp;quot;НеИзвестен&amp;quot;.&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
В динамических классах начальное значение вместо декларации можно задать в клаузе конструктора&lt;br /&gt;
&amp;lt;vip&amp;gt;implement dynamicClass &lt;br /&gt;
domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  персона_V:мойДомен.&lt;br /&gt;
clauses&lt;br /&gt;
  new():-&lt;br /&gt;
     персона_V:=персона(&amp;quot;Старик&amp;quot;,80),&lt;br /&gt;
     ...&lt;br /&gt;
end implement dynamicClass&amp;lt;/vip&amp;gt;&lt;br /&gt;
Бывает, что конкретное начальное значение установить невозможно по смыслу реализации.&lt;br /&gt;
Тогда начальное значение может быть установлено как &amp;quot;неустановленное&amp;quot;.&lt;br /&gt;
&amp;lt;vip&amp;gt;class facts&lt;br /&gt;
  город_V:string:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
где &amp;#039;&amp;#039;erroneous&amp;#039;&amp;#039; - ключевое слово языка. Факт со значением erroneous не может быть использован (точнее: при попытке его использования будет выдана соответствующая ошибка), можно только проверить является ли значение факта неустановленным с помощью детерминированного предиката &amp;#039;&amp;#039;isErroneous(...)&amp;#039;&amp;#039; &lt;br /&gt;
&amp;lt;vip&amp;gt;implement dynamicClass &lt;br /&gt;
domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  город_V:string:=erroneous.&lt;br /&gt;
  персона_V:мойДомен.&lt;br /&gt;
clauses&lt;br /&gt;
  new():-&lt;br /&gt;
     персона_V:=персона(&amp;quot;Старик&amp;quot;,80),&lt;br /&gt;
     ...&lt;br /&gt;
clauses&lt;br /&gt;
  ...&lt;br /&gt;
     isErroneous(город_V),&lt;br /&gt;
     ...&lt;br /&gt;
end implement dynamicClass&amp;lt;/vip&amp;gt;&lt;br /&gt;
С учетом сказанного наш пример&lt;br /&gt;
&amp;lt;vip&amp;gt;class facts&lt;br /&gt;
  город_V:string Город.&amp;lt;/vip&amp;gt;&lt;br /&gt;
на этапе компиляции выдаст ошибку, поскольку статический факт не инициализирован при объявлении.&lt;br /&gt;
&lt;br /&gt;
==Константы и домены==&lt;br /&gt;
Константы и домены могут объявляться везде - в декларациях классов, интерфейсов и в имплементациях. По своей сущности они, естественно, имеют статический характер.&lt;br /&gt;
Объявления, сделанные в имплементации, видимы только внутри этой же имплементации.&lt;br /&gt;
Объявления констант и доменов, сделанные в декларации класса или интерфейса, являются глобальными. &lt;br /&gt;
Конкретное использование осуществляется путем добавления префикса декларации соответствующего класса или интерфейса в формате&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;lt;имя декларации класса или интерфейса&amp;gt;::&amp;lt;имя домена или константы&amp;gt;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Взаимоотношени статических и динамических сущностей==&lt;br /&gt;
Клаузы предикатов, объявленных статическими, не могут обращаться к динамическим сущностям (предикатам и фактам).&lt;br /&gt;
То есть нельзя так:&lt;br /&gt;
&amp;lt;vip&amp;gt;implement dynamicClass &lt;br /&gt;
clauses&lt;br /&gt;
  staticPred():-&lt;br /&gt;
    ...&lt;br /&gt;
    dynamicPred(),&lt;br /&gt;
    ...&lt;br /&gt;
end implement dynamicClass&amp;lt;/vip&amp;gt;&lt;br /&gt;
Клаузы предикатов, объявленных динамическими, такого ограничения не имеют.&lt;br /&gt;
Как статические так и динамические факты могут хранить указатели на динамические предикаты и экземпляры классов. &lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D1%8B._%D0%A1%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%B8_%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=2187</id>
		<title>Классы. Статические и динамические сущности</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D1%8B._%D0%A1%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%B8_%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=2187"/>
		<updated>2008-08-08T08:40:31Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: очепятки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Классы==&lt;br /&gt;
Каждый класс должен иметь декларацию класса и имплементацию.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Декларация класса&amp;#039;&amp;#039;&amp;#039; содержит набор объявлений констант, доменов, предикатов, видимых извне.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Имплементация класса&amp;#039;&amp;#039;&amp;#039; содержит &lt;br /&gt;
*собственно реализацию предикатов (клаузы), объявленных в декларации,&lt;br /&gt;
*декларации констант,&lt;br /&gt;
*декларации и реализации предикатов, используемых в клаузах,&lt;br /&gt;
*декларации фактов. &lt;br /&gt;
*Факты и предикаты, объявленные в имплементации класса, извне не видны.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cтатический класс&amp;#039;&amp;#039;&amp;#039; - класс не способный порождать объекты (экземпляры или точные копии). &lt;br /&gt;
Все предикаты и разделы фактов статического класса существуют в единственном экземпляре во всем проекте. &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Динамический класс&amp;#039;&amp;#039;&amp;#039; - класс, способный порождать объекты (экземпляры или точные копии),&lt;br /&gt;
Динамические классы могут содержать как статические, так и динамические предикаты, а также статические и динамические факты. &lt;br /&gt;
Каждый экземпляр динамического класса содержит свою копию динамического предиката и свою копию динамического раздела фактов. &lt;br /&gt;
Создание объекта (экземпляра или копии класса) осуществляется вызовом предиката-конструктора. Как правило, это предикат-функция &amp;#039;&amp;#039;new()&amp;#039;&amp;#039;, но может быть и любой другой, объявленный как конструктор в разделе &amp;#039;&amp;#039;constructors&amp;#039;&amp;#039; декларации класса. &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Статический класс&amp;#039;&amp;#039;&amp;#039; - это класс, не имеющий интерфейса. &lt;br /&gt;
Или, что то же самое, - если класс не имеет инерфейса, то он - статический, &lt;br /&gt;
&lt;br /&gt;
Если класс имеет интерфейс - то он - динамический. &lt;br /&gt;
&lt;br /&gt;
==Статические классы==&lt;br /&gt;
Например, класс &amp;#039;&amp;#039;staticClass&amp;#039;&amp;#039; является статическим классом. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
class staticClass&lt;br /&gt;
end class staticClass&lt;br /&gt;
&lt;br /&gt;
implement staticClass&lt;br /&gt;
end implement staticClass&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
но этот класс ничего не способен делать. &lt;br /&gt;
Чтобы что-то делать, добавим в этот класс декларации предикатов и клаузы. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class staticClass&lt;br /&gt;
predicates&lt;br /&gt;
   visibleStaticPred:().&lt;br /&gt;
end class staticClass&lt;br /&gt;
&lt;br /&gt;
implement staticClass&lt;br /&gt;
clauses&lt;br /&gt;
     visibleStaticPred():-&lt;br /&gt;
       ...&lt;br /&gt;
       invisibleStaticPred(),&lt;br /&gt;
       ...         &lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
   invisibleStaticPred:().&lt;br /&gt;
clauses&lt;br /&gt;
   invisibleStaticPred():-&lt;br /&gt;
       ...&lt;br /&gt;
end implement staticClass&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Обращения к предикатам этого класса записываются как &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
staticClass::staticVisiblePred(),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
(существенно использования именно &amp;#039;&amp;#039;&amp;#039;::&amp;#039;&amp;#039;&amp;#039; для обращения к статической сущности) &lt;br /&gt;
&lt;br /&gt;
Здесь предикат &amp;#039;&amp;#039;invisibleStaticPred&amp;#039;&amp;#039; является предикатом, объявленным внутри имплементации, и только внутри этой имплементации может быть вызван. &lt;br /&gt;
То есть предикаты, объявленные в декларации класса, являются заведомо всегда статическими предикатами, а раздел объявлений предикатов внутри имплементации статического класса всегда должен имет вид class predicates &lt;br /&gt;
Если попробовать создать экземпляр класса &amp;#039;&amp;#039;staticClass&amp;#039;&amp;#039; с помощью вызова конструктора &amp;#039;&amp;#039;new()&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
staticClass::new(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
то это не пропустит компилятор. &lt;br /&gt;
Таким образом, статические классы - это просто модули проекта. &lt;br /&gt;
Проект, построенный только на модулях (статических классах) - не имеет никакого отношения к объекто-ориентированной концепции программирования. &lt;br /&gt;
&lt;br /&gt;
==Динамические классы==&lt;br /&gt;
Например, класс &amp;#039;&amp;#039;dynamicClass&amp;#039;&amp;#039; является динамическим классом, что определяется &lt;br /&gt;
конструкцией &amp;#039;&amp;#039;dynamicClass:justInterface&amp;#039;&amp;#039;. &lt;br /&gt;
&amp;lt;vip&amp;gt;class dynamicClass:justInterface&lt;br /&gt;
end class dynamicClass &lt;br /&gt;
&lt;br /&gt;
implement dynamicClass &lt;br /&gt;
end implement dynamicClass &lt;br /&gt;
&lt;br /&gt;
interface justInterface&lt;br /&gt;
end interface justInterface&amp;lt;/vip&amp;gt;&lt;br /&gt;
Опять-таки, синтаксически все правильно, но практически бесполезно. &lt;br /&gt;
Теперь добавим какие-нибудь полезности и вынесем вперед декларацию интерфейса. &lt;br /&gt;
&amp;lt;vip&amp;gt;interface justInterface&lt;br /&gt;
predicates&lt;br /&gt;
   visibleDynamicPred:().&lt;br /&gt;
end interface justInterface&lt;br /&gt;
&lt;br /&gt;
class dynamicClass:justInterface&lt;br /&gt;
predicates&lt;br /&gt;
   visibleStaticPred:().&lt;br /&gt;
end class dynamicClass &lt;br /&gt;
&lt;br /&gt;
implement dynamicClass &lt;br /&gt;
clauses&lt;br /&gt;
   visibleStaticPred():-&lt;br /&gt;
      ...&lt;br /&gt;
      invisibleStaticPred(),&lt;br /&gt;
      ...         &lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
   visibleDynamicPred():-&lt;br /&gt;
     ...&lt;br /&gt;
     invisibleStaticPred(),&lt;br /&gt;
     ...         &lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
   invisibleStaticPred:().&lt;br /&gt;
clauses&lt;br /&gt;
   invisibleStaticPred():-&lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
   invisibleDynamicPred:().&lt;br /&gt;
clauses&lt;br /&gt;
   invisibleDynamicPred():-&lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
end implement dynamicClass&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь мы имеем четыре предиката, которые имеют различные динамические особенности.&lt;br /&gt;
&amp;#039;&amp;#039;visibleDynamicPred&amp;#039;&amp;#039; - динамический предикат, принадлежащий интерфесу &amp;#039;&amp;#039;justInterface&amp;#039;&amp;#039;, видимый извне. &lt;br /&gt;
&amp;#039;&amp;#039;visibleStaticPred&amp;#039;&amp;#039; - статический предикат, видимый извне. &lt;br /&gt;
&amp;#039;&amp;#039;invisibleStaticPred&amp;#039;&amp;#039; - статический предикат, определенный в имплементации класса &amp;#039;&amp;#039;dynamicClass&amp;#039;&amp;#039;, невидимый извне. &lt;br /&gt;
&amp;#039;&amp;#039;invisibleDynamicPred&amp;#039;&amp;#039; - динамический предикат, определенный в имплементации класса &amp;#039;&amp;#039;dynamicClass&amp;#039;&amp;#039;, невидимый извне. &lt;br /&gt;
К такому классу можно обратиться как к статическому &lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
dynamicClass::visibleStaticPred(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
но нельзя так: &lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
dynamicClass::visibleDynamicPred(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Чтобы вызвать предикат &amp;#039;&amp;#039;visibleDynamicPred()&amp;#039;&amp;#039;, необходимо с помощью конструктора создать экземпляр класса и обратиться к этому экземпляру:&lt;br /&gt;
&amp;lt;vip&amp;gt;...&lt;br /&gt;
ЭкземплярDynamicClass=dynamicClass::new(),&lt;br /&gt;
ЭкземплярDynamicClass:visibleDynamicPred(),&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Примечания: &lt;br /&gt;
* Конструктор &amp;#039;&amp;#039;new()&amp;#039;&amp;#039; является предопределенным конструктором, объявление которого не требуется, если в имплементации нет для него клауза; &lt;br /&gt;
* В обращении к экземпляру класса (объекту) используется разделитель &amp;#039;:&amp;#039; (а не &amp;#039;::&amp;#039;, как этого требует обращение к статической сущности).&lt;br /&gt;
&lt;br /&gt;
==Факты==&lt;br /&gt;
Факты декларируются и могут быть использованы в клаузах предикатов только в имплементации класса.&lt;br /&gt;
Нельзя объявить факты в одном классе и пытаться использовать обращение к ним в другом классе.&lt;br /&gt;
В статических классах факты должны быть объявлены только как статические.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;implement example&lt;br /&gt;
...&lt;br /&gt;
class facts&lt;br /&gt;
  myFact_F:(string,string).&lt;br /&gt;
...&lt;br /&gt;
end implement example&amp;lt;/vip&amp;gt;&lt;br /&gt;
В имплементациях динамических классов факты могут быть объявлены либо как статические, либо как динамические:&lt;br /&gt;
&amp;lt;vip&amp;gt;class example1:example1interface&lt;br /&gt;
predicates&lt;br /&gt;
  запомнитьГород:(string Город).&lt;br /&gt;
  запомнитьИмяАдрес:(string Имя, string Адрес).&lt;br /&gt;
  получитьГород:()-&amp;gt;string Город.&lt;br /&gt;
  получитьИмяАдрес:(string Имя, string Адрес).&lt;br /&gt;
end class example1&lt;br /&gt;
&lt;br /&gt;
implement example1&lt;br /&gt;
...&lt;br /&gt;
class facts&lt;br /&gt;
  город_V:string Город.&lt;br /&gt;
...&lt;br /&gt;
facts&lt;br /&gt;
  имяАдрес_F:(string Адрес, string Имя).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  запомнитьГород(Город):-&lt;br /&gt;
    город_V:=Город.&lt;br /&gt;
...&lt;br /&gt;
clauses&lt;br /&gt;
  запомнитьИмяАдрес(Имя,Адрес):-&lt;br /&gt;
    assert(имяАдрес_F:(Адрес,Имя)).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  получитьГород(город_V).&lt;br /&gt;
  ...&lt;br /&gt;
clauses&lt;br /&gt;
  получитьИмяАдрес(Имя,Адрес):-&lt;br /&gt;
    имяАдрес_F(Адрес,Имя),&lt;br /&gt;
    !,&lt;br /&gt;
    ...&lt;br /&gt;
end implement example1&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
В приведенном примере класса &amp;#039;&amp;#039;example1&amp;#039;&amp;#039; факт &amp;#039;&amp;#039;город_V&amp;#039;&amp;#039; является статическим, а факты &amp;#039;&amp;#039;имяАдрес_F(...)&amp;#039;&amp;#039; являются динамическими,&lt;br /&gt;
что определяется разницей в имени раздела &amp;#039;&amp;#039;class facts&amp;#039;&amp;#039; и просто &amp;#039;&amp;#039;facts&amp;#039;&amp;#039;.&lt;br /&gt;
Поскольку статическая сущность любого класса всего одна, а динамическая - по одной на каждый экземпляр - то и в нашем примере запрос &amp;#039;&amp;#039;получитьГород()&amp;#039;&amp;#039;, переданный любому экземпляру, вернет один и тот же результат, а запрос &amp;#039;&amp;#039;получитьИмяАдрес(...)&amp;#039;&amp;#039; вернет для каждого экземпляра свое значение.&lt;br /&gt;
===Особенности использования фактов-переменных===&lt;br /&gt;
В примере выше использовался факт-переменная &amp;#039;&amp;#039;город_V&amp;#039;&amp;#039;, особенностью которого является то, что такой факт всегда один и он сам несет значение терма, объявленного в декларации.&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
class facts&lt;br /&gt;
  город_V:string Город.&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь факт &amp;#039;&amp;#039;город_V&amp;#039;&amp;#039; имеет строковое значение. Возможны значения любых объявленных доменов, например&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  персона_V:мойДомен.&amp;lt;/vip&amp;gt;&lt;br /&gt;
В отличие от обычных фактов, факт-переменная всегда существует и должен иметь начальное значение.&lt;br /&gt;
Начальное значение факту-переменной можно задать при объявлении&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  персона_V:мойДомен:=персона(&amp;quot;Старик&amp;quot;,80).&lt;br /&gt;
  город_V:string:=&amp;quot;НеИзвестен&amp;quot;.&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
В динамических классах начальное значение вместо декларации можно задать в клаузе конструктора&lt;br /&gt;
&amp;lt;vip&amp;gt;implement dynamicClass &lt;br /&gt;
domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  персона_V:мойДомен.&lt;br /&gt;
clauses&lt;br /&gt;
  new():-&lt;br /&gt;
     персона_V:=персона(&amp;quot;Старик&amp;quot;,80),&lt;br /&gt;
     ...&lt;br /&gt;
end implement dynamicClass&amp;lt;/vip&amp;gt;&lt;br /&gt;
Бывает, что конкретное начальное значение установить невозможно по смыслу реализации.&lt;br /&gt;
Тогда начальное значение может быть установлено как &amp;quot;неустановленное&amp;quot;.&lt;br /&gt;
&amp;lt;vip&amp;gt;class facts&lt;br /&gt;
  город_V:string:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
где &amp;#039;&amp;#039;erroneous&amp;#039;&amp;#039; - ключевое слово языка. Факт со значением erroneous не может быть использован, можно только проверить является ли значение факта неустановленным с помощью детерминированного предиката &amp;#039;&amp;#039;isErroneous(...)&amp;#039;&amp;#039; &lt;br /&gt;
&amp;lt;vip&amp;gt;implement dynamicClass &lt;br /&gt;
domains&lt;br /&gt;
  мойДомен=персона(string Имя,unsigned Возраст).&lt;br /&gt;
class facts&lt;br /&gt;
  город_V:string:=erroneous.&lt;br /&gt;
  персона_V:мойДомен.&lt;br /&gt;
clauses&lt;br /&gt;
  new():-&lt;br /&gt;
     персона_V:=персона(&amp;quot;Старик&amp;quot;,80),&lt;br /&gt;
     ...&lt;br /&gt;
clauses&lt;br /&gt;
  ...&lt;br /&gt;
     isErroneous(город_V),&lt;br /&gt;
     ...&lt;br /&gt;
end implement dynamicClass&amp;lt;/vip&amp;gt;&lt;br /&gt;
С учетом сказанного наш пример&lt;br /&gt;
&amp;lt;vip&amp;gt;class facts&lt;br /&gt;
  город_V:string Город.&amp;lt;/vip&amp;gt;&lt;br /&gt;
на этапе компиляции выдаст ошибку, поскольку статический факт не инициализирован при объявлении.&lt;br /&gt;
==Константы и домены==&lt;br /&gt;
Константы и домены могут объявляться везде - в декларациях классов, интерфейсов и в имплементациях. По своей сущности они, естественно, имеют статический характер.&lt;br /&gt;
Объявления, сделанные в имплементации, видимы только внутри этой же имплементации.&lt;br /&gt;
Объявления констант и доменов, сделанные в декларации класса или интерфейса, являются глобальными. &lt;br /&gt;
Конкретное использование осуществляется путем добавления префикса декларации соответствующего класса или интерфейса в формате&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;lt;имя декларации класса или интерфейса&amp;gt;::&amp;lt;имя домена или константы&amp;gt;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Взаимоотношени статических и динамических сущностей==&lt;br /&gt;
Клаузы предикатов, объявленных статическими, не могут обращаться к динамическим сущностям (предикатам и фактам).&lt;br /&gt;
То есть нельзя так:&lt;br /&gt;
&amp;lt;vip&amp;gt;implement dynamicClass &lt;br /&gt;
clauses&lt;br /&gt;
  staticPred():-&lt;br /&gt;
    ...&lt;br /&gt;
    dynamicPred(),&lt;br /&gt;
    ...&lt;br /&gt;
end implement dynamicClass&amp;lt;/vip&amp;gt;&lt;br /&gt;
Клаузы предикатов, объявленных динамическими, такого ограничения не имеют.&lt;br /&gt;
Как статические так и динамические факты могут хранить указатели на динамические предикаты и экземпляры классов. &lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=1942</id>
		<title>Обработка исключений</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=1942"/>
		<updated>2008-01-10T11:31:50Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Исключение - это отклонение программы от маршрута &amp;quot;нормального&amp;quot; выполнения. Пакет exception из состава PFC содержит предикаты помогающие обрабатывать и распространять исключения, которые возникают в программах на Прологе. Это руководство объясняет:&lt;br /&gt;
&lt;br /&gt;
*Как обрабатывать исключения;&lt;br /&gt;
*Как возбуждать ваши собственные исключения;&lt;br /&gt;
*Как продолжать другое исключение.&lt;br /&gt;
&lt;br /&gt;
==Как перехватывать исключения==&lt;br /&gt;
&lt;br /&gt;
Рассмотрим программу, которая читает текстовый файл и выводит его содержимое на консоль. PFC предлагает предикат &amp;#039;&amp;#039;&amp;#039;file::readString&amp;#039;&amp;#039;&amp;#039;, который позволяет выполнить эту задачу. Однако, некоторые обстоятельства могут помешать эту задачу; например, заданное имя файла ссылается на несуществующий файл. Когда предикат &amp;#039;&amp;#039;&amp;#039;file::readString&amp;#039;&amp;#039;&amp;#039; не может выполнить задачу, он генерирует исключение.&lt;br /&gt;
&lt;br /&gt;
Visual Prolog предлагает конструкцию &amp;#039;&amp;#039;&amp;#039;try/catch&amp;#039;&amp;#039;&amp;#039; (см. [[#trap vs try|Try вместо Trap]]), которая перехватывает исключение и позволяет его обработать. Детальное описание этой конструкции можно найти в справке по Visual Prolog (&amp;#039;&amp;#039;&amp;#039;Language Reference -&amp;gt; Exception Handling&amp;#039;&amp;#039;&amp;#039;).&lt;br /&gt;
&lt;br /&gt;
Соответственно, код для перехвата исключения будет выглядеть так:&lt;br /&gt;
&amp;lt;vip&amp;gt;try  &lt;br /&gt;
    MyTxtFileContent = file::readString(myFileName, _IsUnicode)&lt;br /&gt;
catch ErrorCode do&lt;br /&gt;
    handleFileReadError(ErrorCode)&lt;br /&gt;
end try,&lt;br /&gt;
...&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Самое интересное здесь это - имплементация предиката handleFileReadError:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class predicates&lt;br /&gt;
    handleFileReadError : (exception::traceID TraceId) failure.&lt;br /&gt;
clauses&lt;br /&gt;
    handleFileReadError(TraceId):-&lt;br /&gt;
        Descriptor = exception::tryGetDescriptor(TraceId, fileSystem_api::cannotcreate),&lt;br /&gt;
        !, % файл не может быт загружен&lt;br /&gt;
        exception::descriptor(_ErrorCode, % ErrorCode используется, если пользователь сам вызывает errorExit.&lt;br /&gt;
            _ClassInfo, % информация о классе, который возбудил исключение.&lt;br /&gt;
            _Exception, % на самом деле это fileSystem_api::cannotcreate,&lt;br /&gt;
            % но параметр не может участвовать в сравнении с помощью &amp;#039; = &amp;#039;.&lt;br /&gt;
            % смотрите exceptionState::equals&lt;br /&gt;
            _Kind, % исключение может быть возбуждено или продолжено&lt;br /&gt;
            ExtraInfo,&lt;br /&gt;
            _TraceId, % здесь идентификатор ошибки, но необходимости в проверке нет&lt;br /&gt;
            _GMTTime, % время возникновения исключения&lt;br /&gt;
            ExceptionDescription,&lt;br /&gt;
            _ThreadId) = Descriptor,&lt;br /&gt;
        FileName = namedValue::mapLookUp(ExtraInfo,&lt;br /&gt;
        fileSystem_api::fileName_parameter, string(&amp;quot;&amp;quot;)),&lt;br /&gt;
        Reason = namedValue::mapLookUp(ExtraInfo,&lt;br /&gt;
        common_exception::errorDescription_parameter, string(&amp;quot;&amp;quot;)),&lt;br /&gt;
        stdIO::write(&amp;quot;Невозможно загрузить файл по причине: &amp;quot;,ExceptionDescription,&lt;br /&gt;
            &amp;quot;\nИмя файла: &amp;quot;, FileName,&lt;br /&gt;
            &amp;quot;\nПричина: &amp;quot;, Reason ),&lt;br /&gt;
        fail.&lt;br /&gt;
    handleFileReadError(ErrorCode):-&lt;br /&gt;
        isDebugMode = true, % режим отладки&lt;br /&gt;
        !,&lt;br /&gt;
        exceptionDump::dumpToStdOutput(ErrorCode),&lt;br /&gt;
        % выводим на консоль, поскольку режим отладки&lt;br /&gt;
        fail.&lt;br /&gt;
    handleFileReadError(_ErrorCode):-&lt;br /&gt;
        % программа не может обработать исключение и не информирует о нем&lt;br /&gt;
        fail.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Когда предполагается исключение &amp;#039;&amp;#039;&amp;#039;fileSystem_api::cannotcreate&amp;#039;&amp;#039;&amp;#039;, предикат &amp;#039;&amp;#039;&amp;#039;exception::tryGetDescriptor&amp;#039;&amp;#039;&amp;#039; вызывается именно с этим параметром для перехвата и обработки такого исключения. В нашем случае используется вывод на консоль:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;stdIO::write(&amp;quot;Невозможно загрузить файл по причине: &amp;quot;, ExceptionDescription,&lt;br /&gt;
    &amp;quot;\nИмяФайла: &amp;quot;, FileName,&lt;br /&gt;
    &amp;quot;\nПричина: &amp;quot;, Reason ),&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с объяснением причины исключения.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что нет необходимости в очистке следов исключения, поскольку вся необходимая информация сохраняется в TraceId. Этот факт значительно ускоряет работу программы.&lt;br /&gt;
&lt;br /&gt;
Полный пример содержится в проекте &amp;#039;&amp;#039;&amp;#039;catchException\catchException.prj6&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Как вызвать исключение==&lt;br /&gt;
Рассмотрим программу, которая проверяет размер заданного файла. PFC предлагает предикат file::getFileProperties, который возвращает размер заданного файла. Если файл имеет нулевой размер, то мы можем вызвать исключение с помощью предиката exception::raise. Необходимо при этом создать предикат, который будет характеризовать исключение. С этой целью мы создаём предикат myExceptionZeroFileSize. Код, вызывающий исключение, будет выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  run():-&lt;br /&gt;
    console::init(),&lt;br /&gt;
    try&lt;br /&gt;
      file::getFileProperties(myFileName,_Attributes,Size,_Creation,_LastAccess,_LastChange)&lt;br /&gt;
    catch ErrorCode do&lt;br /&gt;
      handleFileGetPropertiesError(ErrorCode)&lt;br /&gt;
    end try,&lt;br /&gt;
  &lt;br /&gt;
    Size = unsigned64(0, 0), % file size is zero&lt;br /&gt;
    !,&lt;br /&gt;
    exception::raise&lt;br /&gt;
        (&lt;br /&gt;
        classInfo,&lt;br /&gt;
        myExceptionZeroFileSize,&lt;br /&gt;
        [namedValue(fileSystem_api::fileName_parameter, string(myFileName))]&lt;br /&gt;
        ).&lt;br /&gt;
  run().&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первый параметр предиката exception::raise - предикат classInfo, который генерируется средой  IDE для каждого нового класса. Для генерируемого исключения неплохо было бы определить дополнительную информацию, относящуюся к данному исключению. В нашем случае полезно имя файла, поскольку нулевой  размер связан с этим заданным файлом.&lt;br /&gt;
&lt;br /&gt;
Предикат myExceptionZeroFileSize создаётся по шаблону:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  myExceptionZeroFileSize&lt;br /&gt;
    (&lt;br /&gt;
    classInfo,&lt;br /&gt;
    predicate_Name(),&lt;br /&gt;
    &amp;quot;Размер файла не может быть нулевым&amp;quot;&lt;br /&gt;
    ).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь первый и второй параметры предикатов исключений всегда есть classInfo и predicate_Name() соответственно, а третий параметр содержит текстовое пояснение причины исключения.&lt;br /&gt;
&lt;br /&gt;
Полностью пример представлен в проекте &amp;#039;&amp;#039;&amp;#039;raiseException\raiseException.prj6&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Продолжение другого исключения==&lt;br /&gt;
&lt;br /&gt;
Рассмотрим программу, помещённую в DLL, которая читает текстовый файл и его содержимое передаёт в возвращаемом параметре. Если происходит исключение, то DLL продолжает его и добавляет информацию о версии DLL. Для продолжения исключения код выглядит так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;constants&lt;br /&gt;
  dllVersion = &amp;quot;1.20.0.0&amp;quot;.&lt;br /&gt;
clauses&lt;br /&gt;
  loadFile(FileName) = FileContent :-&lt;br /&gt;
    try&lt;br /&gt;
       FileContent = file::readString(FileName, _IsUnicode)&lt;br /&gt;
    catch ErrorCode do&lt;br /&gt;
       exception::continue(ErrorCode, classInfo, continuedFromDll,&lt;br /&gt;
       [namedValue(version_Parameter,string(dllVersion))])&lt;br /&gt;
    end try.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат continuedFromDll объявлен как:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;predicates&lt;br /&gt;
  continuedFromDll : core::exception as &amp;quot;_ContinuedFromDll@12&amp;quot;&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
и этот предикат экспортируется из DLL. Имплементация предиката continuedFromDll создаётся по шаблону (аналогично предикату myExceptionZeroFileSizeabove):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  continuedFromDll&lt;br /&gt;
    (&lt;br /&gt;
    classInfo,&lt;br /&gt;
    predicate_Name(),&lt;br /&gt;
    &amp;quot;Exception continued from continueException.DLL&amp;quot;&lt;br /&gt;
    ).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При продолжении исключения предикат exception::continue добавляет информацию о версии DLL.&lt;br /&gt;
&lt;br /&gt;
Полностью пример представлен в проекте &amp;#039;&amp;#039;&amp;#039;continueException\continueException.prj6&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим теперь, как перехватывать такого рода исключения. Код в принципе похож на обсуждавшийся выше, поэтому мы сосредоточимся на различиях.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;constants&lt;br /&gt;
  myFileName = &amp;quot;my.txt&amp;quot;.&lt;br /&gt;
clauses&lt;br /&gt;
  run():-&lt;br /&gt;
    console::init(),&lt;br /&gt;
    initExceptionState(exception::getExceptionState()),&lt;br /&gt;
    try&lt;br /&gt;
      MyTxtFileContent = loadFile(myFileName)&lt;br /&gt;
    catch TraceID do&lt;br /&gt;
      handleFileReadError(TraceID)&lt;br /&gt;
    end try,&lt;br /&gt;
    !,&lt;br /&gt;
    stdIO::write(&amp;quot;The content of &amp;quot;,myFileName,&amp;quot;is:\n&amp;quot;,MyTxtFileContent).&lt;br /&gt;
  run().&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
  handleFileReadError : ( exception::traceID TraceId )failure .&lt;br /&gt;
clauses&lt;br /&gt;
  handleFileReadError(TraceId):-&lt;br /&gt;
    DescriptorContinued = exception::tryGetDescriptor(TraceId, continuedFromDll),&lt;br /&gt;
    ErrorDescriptorInformation = exception::tryGetErrorDescriptorInformation(TraceId),&lt;br /&gt;
    ContinueException = ErrorDescriptorInformation:tryGetContinueException(),&lt;br /&gt;
    !, % ясно, что файл не загрузился&lt;br /&gt;
    exception::descriptor&lt;br /&gt;
      (_ErrorCodeContinued,&lt;br /&gt;
      _ClassInfoContinued,&lt;br /&gt;
      _ExceptionContinued,&lt;br /&gt;
      _KindContinued,&lt;br /&gt;
      ExtraInfoContinued,&lt;br /&gt;
      _,&lt;br /&gt;
      _GMTTimeContinued,&lt;br /&gt;
      ExceptionDescriptionContinued,&lt;br /&gt;
      _ThreadIdContinued) = DescriptorContinued,&lt;br /&gt;
      Version = namedValue::mapLookUp(ExtraInfoContinued, version_Parameter, string(&amp;quot;&amp;quot;)),&lt;br /&gt;
      stdIO::write(&amp;quot;Продолженное исключение : &amp;quot;,ExceptionDescriptionContinued,&amp;quot;\nВерсия Dll: &amp;quot;, Version,&amp;quot;\n&amp;quot;),&lt;br /&gt;
      ExtraInfo = ContinueException:getExtraInfo(),&lt;br /&gt;
      ExceptionDescription = ContinueException:getExceptionDescription(),&lt;br /&gt;
      FileName = namedValue::mapLookUp(ExtraInfo,fileSystem_api::fileName_parameter, string(&amp;quot;&amp;quot;)),&lt;br /&gt;
      Reason = namedValue::mapLookUp(ExtraInfo,common_exception::errorDescription_parameter, string(&amp;quot;&amp;quot;)),&lt;br /&gt;
      stdIO::write&lt;br /&gt;
        (&lt;br /&gt;
        &amp;quot;Файл не загружен по причине: &amp;quot;,ExceptionDescription,&lt;br /&gt;
        &amp;quot;\nИмя файла: &amp;quot;, FileName,&lt;br /&gt;
        &amp;quot;\nПричина: &amp;quot;, Reason ),&lt;br /&gt;
      fail.&lt;br /&gt;
  handleFileReadError(TraceId):-&lt;br /&gt;
    isDebugMode = true, % режим отладки&lt;br /&gt;
    !,&lt;br /&gt;
    exceptionDump::dumpToStdOutput(TraceId),&lt;br /&gt;
    fail.&lt;br /&gt;
  handleFileReadError(_TraceId):-&lt;br /&gt;
    fail.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы пытаемся здесь найти исключение &amp;#039;&amp;#039;&amp;#039;continuedFromDll&amp;#039;&amp;#039;&amp;#039; и мы вызываем продолжающееся предикатом исключение &amp;#039;&amp;#039;&amp;#039;tryGetContinueException&amp;#039;&amp;#039;&amp;#039;. Это ясно показывает, что, если исключение продолжающееся, то мы можем получить дополнительную информацию касательно самого исключения.&lt;br /&gt;
&lt;br /&gt;
Полностью пример представлен в проекте &amp;#039;&amp;#039;&amp;#039;continueException\testContinuedException\testContinuedException.prj6&amp;#039;&amp;#039;&amp;#039;. Необходимо построить сначала приложение &amp;#039;&amp;#039;&amp;#039;continueException\continueException.prj6&amp;#039;&amp;#039;&amp;#039; перед тем как вызывать на выполнение исполняемое приложение testContinuedException.&lt;br /&gt;
&lt;br /&gt;
==trap vs try==&lt;br /&gt;
Начиная с версии 7.0 появилась конструкция &amp;#039;&amp;#039;&amp;#039;try/catch/finally&amp;#039;&amp;#039;&amp;#039;, которая заменяет встроенные предикаты &amp;#039;&amp;#039;&amp;#039;trap/3&amp;#039;&amp;#039;&amp;#039; и &amp;#039;&amp;#039;&amp;#039;finally/2&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
[[en:Exceptions Handling]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1856</id>
		<title>Жизненный цикл объекта</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1856"/>
		<updated>2007-12-20T10:17:11Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Финализатор */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Создание объекта===&lt;br /&gt;
Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new().&lt;br /&gt;
&lt;br /&gt;
===Гибель объекта===&lt;br /&gt;
VIP не содержит никаких средств для уничтожения объектов. Тем не менее они не живут вечно (до конца жизни приложения). Время жизни объектов определяется сборщиком мусора (&amp;#039;&amp;#039;GarbageCollector&amp;#039;&amp;#039;), встроенного в &amp;#039;&amp;#039;RunTime&amp;#039;&amp;#039; часть VIP. Сборщик мусора считает, что память, занимаемая объектом (экземпляром), может быть освобождена, когда исчезнут все ссылки на объект. И это почти единственное правило, определяющее время жизни объекта (экземпляра).&lt;br /&gt;
Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(),&lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза().&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь указатель на экземпляр класса &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; сохраняется в переменной &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039;. Поэтому, пока клоз &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; выполняется, живет и доступен через указатель &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039; и экземпляр &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039;.&lt;br /&gt;
По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только в пределах клоза. Следовательно, по завершении выполнения клоза &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; объект&amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; считается недоступным и может умереть. Если, конечно, он не породил ссылок на себя самого в какой-нибудь постоянной памяти во время вызова конструктора или других своих предикатов.&lt;br /&gt;
Случай&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
не меняет сути - пока выполняется предикат &amp;#039;&amp;#039;вызовПредикатаДругогоКлоза(МойОбъект)&amp;#039;&amp;#039; экземпляр продолжает жить и умрет после завершения выполнения предиката &amp;#039;&amp;#039;последнийПредикатКлоза(...)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
В большинстве случаев для построения более или менее полезной и хорошо организованной программы этого недостаточно. Объект, содержащий данные, используемые другими объектами, должен жить, пока это необходимо и должен быть доступен.&lt;br /&gt;
&lt;br /&gt;
Для этой цели используется хранение указателей на объекты в фактах. В VIP как предикаты, так и факты типизированы. А это значит, что при декларировании факта (а равно и предиката) необходимо указание домена - либо предопределённого в языке, либо объявленного в программе.&lt;br /&gt;
Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс.&lt;br /&gt;
&lt;br /&gt;
Поэтому имя интерфейса используется в качестве домена при декларировании фактов для хранения указателей или их передачи в качестве параметров.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;class мойКласс:мойИнтерфейс&lt;br /&gt;
end class мойКласс&amp;lt;/vip&amp;gt;&lt;br /&gt;
уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену &amp;#039;&amp;#039;мойИнтерфейс&amp;#039;&amp;#039;.&lt;br /&gt;
Поэтому объявление факта либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_F:(string УдобноеИмяЭкземпляра,мойИнтерфейс УказательНаЭкземпляр).&amp;lt;/vip&amp;gt;&lt;br /&gt;
либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
Являются правильными (не путать с целесообразностью) объявлениями.&lt;br /&gt;
Тогда в примере&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     мойУказатель_V:=МойОбъект,&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
время жизни объекта продлевается за пределы времени жизни клоза. Объект будет существовать все то время, пока на него существует указатель.&lt;br /&gt;
Когда объект больше не нужен, достаточно удалить ссылку на него, например так:&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:=erroneous,&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
или так&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     retract(мойУказатель_F(&amp;quot;ПочтовыеАдреса&amp;quot;,_УказательНаЭкземпляр)),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом следует иметь в виду, что смерть объекта ведет к смерти всех объектов, на которые данный объект хранит ссылки.&lt;br /&gt;
&lt;br /&gt;
Тогда, например, если имеется дерево, построенное на основе ссылок на объекты, то потеря указателя на корень дерева ведет к смерти всех объектов, входящих в дерево.&lt;br /&gt;
&lt;br /&gt;
Следует, однако, иметь в виду некоторые исключения из этого правила:&lt;br /&gt;
Как показал опыт, если объект хранит указатель на окно (домен &amp;#039;&amp;#039;vpiDomains::windowHandle&amp;#039;&amp;#039;), то сборщик мусора будет рассматривать этот объект, как используемый.&lt;br /&gt;
&lt;br /&gt;
То есть физически окно может быть закрыто, но не удаление указателя на это окно оставит объект в памяти. Аналогично часто указатели на COM-объекты могут быть причиной не освобождения памяти сборщиком мусора.&lt;br /&gt;
&lt;br /&gt;
Существует один замечательный предопределённый интерфейс, который, как и все интерфейсы является и доменом. Это интерфейс - object.&lt;br /&gt;
&lt;br /&gt;
Все экземпляры, принадлежащие доменам своих интерфейсов, в то же время являются дочерними доменами домена object.&lt;br /&gt;
Это значит, что практически возможно вместо&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
объявлять&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:object:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом будет компилироваться и работать&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     мойУказатель_V:=мойОбъект::new(),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Но вот вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
в этом случае не пропустит компилятор.&lt;br /&gt;
Вместо этого необходимо производить конвертацию&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     МойУказатель=convert(мойИнтерфейс,мойУказатель_V),&lt;br /&gt;
     МойУказатель:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы.&lt;br /&gt;
Сборщик мусора можно подтолкнуть предикатом&lt;br /&gt;
&amp;lt;vip&amp;gt;memory::garbageCollect()&amp;lt;/vip&amp;gt; но обычно в этом нет никакой необходимости.&lt;br /&gt;
&lt;br /&gt;
===Финализатор===&lt;br /&gt;
Существует специальный предикат, который вызывается в момент гибели объекта, перед освобождением памяти объекта. Этот предикат - &amp;#039;&amp;#039;&amp;#039;finalize&amp;#039;&amp;#039;&amp;#039;. Его нельзя объявить, но для объектных классов можно написать реализацию (клауз).&lt;br /&gt;
Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат &amp;#039;&amp;#039;&amp;#039;finalize&amp;#039;&amp;#039;&amp;#039; (если он есть), не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой. Также нельзя делать никаких предположений о порядке освобождения объектов и, соответственно, о порядке вызова &amp;#039;&amp;#039;&amp;#039;finalize&amp;#039;&amp;#039;&amp;#039; для разных объектов. Рекомендуется предикат &amp;#039;&amp;#039;&amp;#039;finalize&amp;#039;&amp;#039;&amp;#039; использовать исключительно для освобождения ресурсов, при этом объектные факты освобождать нет никакой необходимости. Раз уж объект удаляется, то и все его факты постигнет аналогичная участь.&lt;br /&gt;
Так же не рекомендуется закладывать в финализатор выполнение каких-нибудь логических действий. Например, если закрытие файла делать в финализаторе, то файл будет отрыт, а доступ других программ будет заблокировано, до неизвестного момента, пока сборщик мусора соизволит удалить данный объект.&lt;br /&gt;
Следует учитывать и то, что финализатор может быть вызван на не полностью инициализированном объекте. Например, если произошёл сбой при выполнении конструктора, то не все факты будут правильно инициализированы. Поэтому всегда следует проверять факт, например на &amp;#039;&amp;#039;&amp;#039;erroneous&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Ещё одно замечание, нельзя заранее сказать на каком потоке (thread) будет выполняться сборщик мусора (неявный вызов) и, соответственно, выполняться финализаторы. Распространение исключения из финализатора может повредить логику работы сборщика. В связи с эти все исключения из финализаторов перехватываются [[Обработка исключений|try/catch]] и игнорируется. Т.е. как будто вызов финализатора происходит так:&lt;br /&gt;
&amp;lt;Vip&amp;gt;try&lt;br /&gt;
  Object:finalize()&lt;br /&gt;
catch _ do&lt;br /&gt;
  succeed()&lt;br /&gt;
end try&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=ProfileCount&amp;diff=1849</id>
		<title>ProfileCount</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=ProfileCount&amp;diff=1849"/>
		<updated>2007-12-19T13:28:37Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для измерения времени выполнения предикатов можно использовать класс &amp;#039;&amp;#039;&amp;#039;profileCount&amp;#039;&amp;#039;&amp;#039; из пакета &amp;#039;&amp;#039;&amp;#039;pfc\profile&amp;#039;&amp;#039;&amp;#039;. Перед его использованием необходимо добавить опцию компилятора &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039; и перестроить проект. Также необходимо вставить вызовы запускающие профилирование &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039;, обычно при инициализации приложения, останова &amp;#039;&amp;#039;&amp;#039;stop&amp;#039;&amp;#039;&amp;#039; и вывода результатов &amp;#039;&amp;#039;&amp;#039;print&amp;#039;&amp;#039;&amp;#039; (обычно в конце приложения). &lt;br /&gt;
&lt;br /&gt;
Вот типичный пример использования в консольном приложении:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
clauses&lt;br /&gt;
    run():-&lt;br /&gt;
        console::init(),&lt;br /&gt;
        profileCount::start(),        &lt;br /&gt;
        try&lt;br /&gt;
            главныйПредикат()&lt;br /&gt;
        finally        &lt;br /&gt;
            profileCount::stop(),&lt;br /&gt;
            Out = console::getConsoleOutputStream(),&lt;br /&gt;
            profileCount::print(Out)&lt;br /&gt;
        end try.&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для всех предикатов выводится количество их вызовов, а для некоторых и суммарное время выполнения. Следует учесть, что данный инструмент (как впрочем и все) влияет на выполняемую программу (даже если предикат &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039; не был выполнен). Программа становится больше и выполняется медленней. Это надо учитывать при анализе полученных результатов. {{note|picsize=30|content=Самое главное, не забыть убрать опцию &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039; после, того, как все замеры будут произведены.}} Если модуль построен без опции &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039;, то предикат &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039; не имеет никакого эффекта.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пример результат работы:&lt;br /&gt;
&lt;br /&gt;
 Predicate invocations count information:&lt;br /&gt;
    1.                           outputStream::write/0...        1   0.00017656&lt;br /&gt;
    2.                         bufferSupport::getBuffer/2        2   0.00014974&lt;br /&gt;
    3.                           outputStream::write/0...        1   0.00009526&lt;br /&gt;
    4.           outputStream_console::writeToStdDevice/4        2   0.00007683&lt;br /&gt;
    5.                              outputStream::flush/0        1   0.00007599&lt;br /&gt;
    6.               outputStream_console::bufferFullCB/2        2   0.00006397&lt;br /&gt;
    7.                                   list::filter/2-&amp;gt;        1   0.00003436&lt;br /&gt;
    8.                                      авто::new/1-&amp;gt;        3   0.00003241&lt;br /&gt;
    9.                             list::getMember_nd/1-&amp;gt;        6   0.00002123&lt;br /&gt;
   10.                          fileSystem_api::lf_crlf/4        2   0.00002039&lt;br /&gt;
   11.    outputStream_console::convertLF2CRLF_ifNeeded/4        2   0.00001062&lt;br /&gt;
   12.                                      main::run$1/0        3   0.00000726&lt;br /&gt;
   13.                  fileSystem_api::searchLineFeeds/6        2   0.00000698&lt;br /&gt;
   14.                                     авто::цена/0-&amp;gt;        4   0.00000670&lt;br /&gt;
   15.                                stream::getMode/0-&amp;gt;        4   0.00000615&lt;br /&gt;
   16.                                console::write/0...        1   0.00000615&lt;br /&gt;
   17.                                        авто::new/1        3   0.00000475&lt;br /&gt;
   18.         fileSystem_api::convertToCRLF_IfNeeded/4-&amp;gt;        2   0.00000419&lt;br /&gt;
   19.                      console_api::getStdHandle/1-&amp;gt;        2   0.00000391&lt;br /&gt;
   20.                        fileSystem_api::getSize/2-&amp;gt;        2   0.00000363&lt;br /&gt;
   21.                        outputSupport::doNotFlush/0        1   0.00000307&lt;br /&gt;
   22.                console::getConsoleOutputStream/0-&amp;gt;        1   0.00000251&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Категория:Profile]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=ProfileCount&amp;diff=1848</id>
		<title>ProfileCount</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=ProfileCount&amp;diff=1848"/>
		<updated>2007-12-19T13:27:42Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для измерения времени выполнения предикатов можно использовать класс &amp;#039;&amp;#039;&amp;#039;profileCount&amp;#039;&amp;#039;&amp;#039; из пакета &amp;#039;&amp;#039;&amp;#039;pfc\profile&amp;#039;&amp;#039;&amp;#039;. Перед его использованием необходимо добавить опцию компилятора &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039; и перестроить проект. Также необходимо вставить вызовы запускающие профилирование &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039;, обычно при инициализации приложения, останова &amp;#039;&amp;#039;&amp;#039;stop&amp;#039;&amp;#039;&amp;#039; и вывода результатов &amp;#039;&amp;#039;&amp;#039;print&amp;#039;&amp;#039;&amp;#039; (обычно в конце приложения). &lt;br /&gt;
&lt;br /&gt;
Вот типичный пример использования в консольном приложении:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
clauses&lt;br /&gt;
    run():-&lt;br /&gt;
        console::init(),&lt;br /&gt;
        profileCount::start(),        &lt;br /&gt;
        try&lt;br /&gt;
            главныйПредикат()&lt;br /&gt;
        finally        &lt;br /&gt;
            profileCount::stop(),&lt;br /&gt;
            Out = console::getConsoleOutputStream(),&lt;br /&gt;
            profileCount::print(Out)&lt;br /&gt;
        end try.&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для всех предикатов выводится количество их вызовов, а для некоторых и суммарное время выполнения. Следует учесть, что данный инструмент (как впрочем и все) влияет на выполняемую программу (даже если предикат &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039; не был выполнен). Программа становится больше и выполняется медленней. Это надо учитывать при анализе полученных результатов. {{note|picsize=30|content=Самое главное, не забыть убрать опцию &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039; после, того, как все замеры будут произведены.}} Если модуль построен без опции &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039;, то предикат &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039; не имеет никакого эффекта.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пример результат работы:&lt;br /&gt;
&lt;br /&gt;
 Predicate invocations count information:&lt;br /&gt;
    1.                           outputStream::write/0...        1   0.00017656&lt;br /&gt;
    2.                         bufferSupport::getBuffer/2        2   0.00014974&lt;br /&gt;
    3.                           outputStream::write/0...        1   0.00009526&lt;br /&gt;
    4.           outputStream_console::writeToStdDevice/4        2   0.00007683&lt;br /&gt;
    5.                              outputStream::flush/0        1   0.00007599&lt;br /&gt;
    6.               outputStream_console::bufferFullCB/2        2   0.00006397&lt;br /&gt;
    7.                                   list::filter/2-&amp;gt;        1   0.00003436&lt;br /&gt;
    8.                                      авто::new/1-&amp;gt;        3   0.00003241&lt;br /&gt;
    9.                             list::getMember_nd/1-&amp;gt;        6   0.00002123&lt;br /&gt;
   10.                          fileSystem_api::lf_crlf/4        2   0.00002039&lt;br /&gt;
   11.    outputStream_console::convertLF2CRLF_ifNeeded/4        2   0.00001062&lt;br /&gt;
   12.                                      main::run$1/0        3   0.00000726&lt;br /&gt;
   13.                  fileSystem_api::searchLineFeeds/6        2   0.00000698&lt;br /&gt;
   14.                                     авто::цена/0-&amp;gt;        4   0.00000670&lt;br /&gt;
   15.                                stream::getMode/0-&amp;gt;        4   0.00000615&lt;br /&gt;
   16.                                console::write/0...        1   0.00000615&lt;br /&gt;
   17.                                        авто::new/1        3   0.00000475&lt;br /&gt;
   18.         fileSystem_api::convertToCRLF_IfNeeded/4-&amp;gt;        2   0.00000419&lt;br /&gt;
   19.                      console_api::getStdHandle/1-&amp;gt;        2   0.00000391&lt;br /&gt;
   20.                        fileSystem_api::getSize/2-&amp;gt;        2   0.00000363&lt;br /&gt;
   21.                        outputSupport::doNotFlush/0        1   0.00000307&lt;br /&gt;
   22.                console::getConsoleOutputStream/0-&amp;gt;        1   0.00000251&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Категория:Profile]]&lt;br /&gt;
[[Категория:VipPfc]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1847</id>
		<title>Жизненный цикл объекта</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1847"/>
		<updated>2007-12-19T08:31:35Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Финализатор */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Создание объекта===&lt;br /&gt;
Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new().&lt;br /&gt;
&lt;br /&gt;
===Гибель объекта===&lt;br /&gt;
VIP не содержит никаких средств для уничтожения объектов. Тем не менее они не живут вечно (до конца жизни приложения). Время жизни объектов определяется сборщиком мусора (&amp;#039;&amp;#039;GarbageCollector&amp;#039;&amp;#039;), встроенного в &amp;#039;&amp;#039;RunTime&amp;#039;&amp;#039; часть VIP. Сборщик мусора считает, что память, занимаемая объектом (экземпляром), может быть освобождена, когда исчезнут все ссылки на объект. И это почти единственное правило, определяющее время жизни объекта (экземпляра).&lt;br /&gt;
Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(),&lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза().&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь указатель на экземпляр класса &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; сохраняется в переменной &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039;. Поэтому, пока клоз &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; выполняется, живет и доступен через указатель &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039; и экземпляр &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039;.&lt;br /&gt;
По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только в пределах клоза. Следовательно, по завершении выполнения клоза &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; объект&amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; считается недоступным и может умереть. Если, конечно, он не породил ссылок на себя самого в какой-нибудь постоянной памяти во время вызова конструктора или других своих предикатов.&lt;br /&gt;
Случай&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
не меняет сути - пока выполняется предикат &amp;#039;&amp;#039;вызовПредикатаДругогоКлоза(МойОбъект)&amp;#039;&amp;#039; экземпляр продолжает жить и умрет после завершения выполнения предиката &amp;#039;&amp;#039;последнийПредикатКлоза(...)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
В большинстве случаев для построения более или менее полезной и хорошо организованной программы этого недостаточно. Объект, содержащий данные, используемые другими объектами, должен жить, пока это необходимо и должен быть доступен.&lt;br /&gt;
&lt;br /&gt;
Для этой цели используется хранение указателей на объекты в фактах. В VIP как предикаты, так и факты типизированы. А это значит, что при декларировании факта (а равно и предиката) необходимо указание домена - либо предопределённого в языке, либо объявленного в программе.&lt;br /&gt;
Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс.&lt;br /&gt;
&lt;br /&gt;
Поэтому имя интерфейса используется в качестве домена при декларировании фактов для хранения указателей или их передачи в качестве параметров.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;class мойКласс:мойИнтерфейс&lt;br /&gt;
end class мойКласс&amp;lt;/vip&amp;gt;&lt;br /&gt;
уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену &amp;#039;&amp;#039;мойИнтерфейс&amp;#039;&amp;#039;.&lt;br /&gt;
Поэтому объявление факта либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_F:(string УдобноеИмяЭкземпляра,мойИнтерфейс УказательНаЭкземпляр).&amp;lt;/vip&amp;gt;&lt;br /&gt;
либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
Являются правильными (не путать с целесообразностью) объявлениями.&lt;br /&gt;
Тогда в примере&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     мойУказатель_V:=МойОбъект,&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
время жизни объекта продлевается за пределы времени жизни клоза. Объект будет существовать все то время, пока на него существует указатель.&lt;br /&gt;
Когда объект больше не нужен, достаточно удалить ссылку на него, например так:&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:=erroneous,&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
или так&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     retract(мойУказатель_F(&amp;quot;ПочтовыеАдреса&amp;quot;,_УказательНаЭкземпляр)),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом следует иметь в виду, что смерть объекта ведет к смерти всех объектов, на которые данный объект хранит ссылки.&lt;br /&gt;
&lt;br /&gt;
Тогда, например, если имеется дерево, построенное на основе ссылок на объекты, то потеря указателя на корень дерева ведет к смерти всех объектов, входящих в дерево.&lt;br /&gt;
&lt;br /&gt;
Следует, однако, иметь в виду некоторые исключения из этого правила:&lt;br /&gt;
Как показал опыт, если объект хранит указатель на окно (домен &amp;#039;&amp;#039;vpiDomains::windowHandle&amp;#039;&amp;#039;), то сборщик мусора будет рассматривать этот объект, как используемый.&lt;br /&gt;
&lt;br /&gt;
То есть физически окно может быть закрыто, но не удаление указателя на это окно оставит объект в памяти. Аналогично часто указатели на COM-объекты могут быть причиной не освобождения памяти сборщиком мусора.&lt;br /&gt;
&lt;br /&gt;
Существует один замечательный предопределённый интерфейс, который, как и все интерфейсы является и доменом. Это интерфейс - object.&lt;br /&gt;
&lt;br /&gt;
Все экземпляры, принадлежащие доменам своих интерфейсов, в то же время являются дочерними доменами домена object.&lt;br /&gt;
Это значит, что практически возможно вместо&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
объявлять&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:object:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом будет компилироваться и работать&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     мойУказатель_V:=мойОбъект::new(),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Но вот вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
в этом случае не пропустит компилятор.&lt;br /&gt;
Вместо этого необходимо производить конвертацию&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     МойУказатель=convert(мойИнтерфейс,мойУказатель_V),&lt;br /&gt;
     МойУказатель:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы.&lt;br /&gt;
Сборщик мусора можно подтолкнуть предикатом&lt;br /&gt;
&amp;lt;vip&amp;gt;memory::garbageCollect()&amp;lt;/vip&amp;gt; но обычно в этом нет никакой необходимости.&lt;br /&gt;
&lt;br /&gt;
===Финализатор===&lt;br /&gt;
Существует специальный предикат, который вызывается в момент гибели объекта, перед освобождением памяти объекта. Этот предикат - finalize. Его нельзя объявить, но для объектных классов можно написать реализацию (клауз).&lt;br /&gt;
Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат finalize (если он есть), не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой. Также нельзя делать никаких предположений о порядке освобождения объектов и, соответственно, о порядке вызова finalize для разных объектов. Рекомендуется предикат finalize использовать исключительно для освобождения ресурсов, при этом объектные факты освобождать нет никакой необходимости. Раз уж объект удаляется, то и все его факты постигнет аналогичная участь.&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1846</id>
		<title>Жизненный цикл объекта</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1846"/>
		<updated>2007-12-19T08:14:39Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Создание объекта===&lt;br /&gt;
Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new().&lt;br /&gt;
&lt;br /&gt;
===Гибель объекта===&lt;br /&gt;
VIP не содержит никаких средств для уничтожения объектов. Тем не менее они не живут вечно (до конца жизни приложения). Время жизни объектов определяется сборщиком мусора (&amp;#039;&amp;#039;GarbageCollector&amp;#039;&amp;#039;), встроенного в &amp;#039;&amp;#039;RunTime&amp;#039;&amp;#039; часть VIP. Сборщик мусора считает, что память, занимаемая объектом (экземпляром), может быть освобождена, когда исчезнут все ссылки на объект. И это почти единственное правило, определяющее время жизни объекта (экземпляра).&lt;br /&gt;
Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(),&lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза().&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь указатель на экземпляр класса &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; сохраняется в переменной &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039;. Поэтому, пока клоз &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; выполняется, живет и доступен через указатель &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039; и экземпляр &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039;.&lt;br /&gt;
По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только в пределах клоза. Следовательно, по завершении выполнения клоза &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; объект&amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; считается недоступным и может умереть. Если, конечно, он не породил ссылок на себя самого в какой-нибудь постоянной памяти во время вызова конструктора или других своих предикатов.&lt;br /&gt;
Случай&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
не меняет сути - пока выполняется предикат &amp;#039;&amp;#039;вызовПредикатаДругогоКлоза(МойОбъект)&amp;#039;&amp;#039; экземпляр продолжает жить и умрет после завершения выполнения предиката &amp;#039;&amp;#039;последнийПредикатКлоза(...)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
В большинстве случаев для построения более или менее полезной и хорошо организованной программы этого недостаточно. Объект, содержащий данные, используемые другими объектами, должен жить, пока это необходимо и должен быть доступен.&lt;br /&gt;
&lt;br /&gt;
Для этой цели используется хранение указателей на объекты в фактах. В VIP как предикаты, так и факты типизированы. А это значит, что при декларировании факта (а равно и предиката) необходимо указание домена - либо предопределённого в языке, либо объявленного в программе.&lt;br /&gt;
Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс.&lt;br /&gt;
&lt;br /&gt;
Поэтому имя интерфейса используется в качестве домена при декларировании фактов для хранения указателей или их передачи в качестве параметров.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;class мойКласс:мойИнтерфейс&lt;br /&gt;
end class мойКласс&amp;lt;/vip&amp;gt;&lt;br /&gt;
уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену &amp;#039;&amp;#039;мойИнтерфейс&amp;#039;&amp;#039;.&lt;br /&gt;
Поэтому объявление факта либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_F:(string УдобноеИмяЭкземпляра,мойИнтерфейс УказательНаЭкземпляр).&amp;lt;/vip&amp;gt;&lt;br /&gt;
либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
Являются правильными (не путать с целесообразностью) объявлениями.&lt;br /&gt;
Тогда в примере&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     мойУказатель_V:=МойОбъект,&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
время жизни объекта продлевается за пределы времени жизни клоза. Объект будет существовать все то время, пока на него существует указатель.&lt;br /&gt;
Когда объект больше не нужен, достаточно удалить ссылку на него, например так:&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:=erroneous,&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
или так&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     retract(мойУказатель_F(&amp;quot;ПочтовыеАдреса&amp;quot;,_УказательНаЭкземпляр)),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом следует иметь в виду, что смерть объекта ведет к смерти всех объектов, на которые данный объект хранит ссылки.&lt;br /&gt;
&lt;br /&gt;
Тогда, например, если имеется дерево, построенное на основе ссылок на объекты, то потеря указателя на корень дерева ведет к смерти всех объектов, входящих в дерево.&lt;br /&gt;
&lt;br /&gt;
Следует, однако, иметь в виду некоторые исключения из этого правила:&lt;br /&gt;
Как показал опыт, если объект хранит указатель на окно (домен &amp;#039;&amp;#039;vpiDomains::windowHandle&amp;#039;&amp;#039;), то сборщик мусора будет рассматривать этот объект, как используемый.&lt;br /&gt;
&lt;br /&gt;
То есть физически окно может быть закрыто, но не удаление указателя на это окно оставит объект в памяти. Аналогично часто указатели на COM-объекты могут быть причиной не освобождения памяти сборщиком мусора.&lt;br /&gt;
&lt;br /&gt;
Существует один замечательный предопределённый интерфейс, который, как и все интерфейсы является и доменом. Это интерфейс - object.&lt;br /&gt;
&lt;br /&gt;
Все экземпляры, принадлежащие доменам своих интерфейсов, в то же время являются дочерними доменами домена object.&lt;br /&gt;
Это значит, что практически возможно вместо&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
объявлять&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:object:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом будет компилироваться и работать&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     мойУказатель_V:=мойОбъект::new(),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Но вот вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
в этом случае не пропустит компилятор.&lt;br /&gt;
Вместо этого необходимо производить конвертацию&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     МойУказатель=convert(мойИнтерфейс,мойУказатель_V),&lt;br /&gt;
     МойУказатель:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы.&lt;br /&gt;
Сборщик мусора можно подтолкнуть предикатом&lt;br /&gt;
&amp;lt;vip&amp;gt;memory::garbageCollect()&amp;lt;/vip&amp;gt; но обычно в этом нет никакой необходимости.&lt;br /&gt;
&lt;br /&gt;
===Финализатор===&lt;br /&gt;
Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат finalize (если он есть), не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой. Также нельзя делать никаких предположений о порядке освобождения объектов и, соответственно, о порядке вызова finalize для разных объектов. Рекомендуется предикат finalize использовать исключительно для освобождения ресурсов, при этом объектные факты освобождать нет никакой необходимости. Раз уж объект удаляется, то и все его факты постигнет аналогичная участь.&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1845</id>
		<title>Жизненный цикл объекта</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1845"/>
		<updated>2007-12-18T21:56:44Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Создание объекта===&lt;br /&gt;
Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new().&lt;br /&gt;
&lt;br /&gt;
===Гибель объекта===&lt;br /&gt;
VIP не содержит никаких средств для уничтожения объектов. Тем не менее они не живут вечно (до конца жизни приложения). Время жизни объектов определяется сборщиком мусора (&amp;#039;&amp;#039;GarbageCollector&amp;#039;&amp;#039;), встроенного в &amp;#039;&amp;#039;RunTime&amp;#039;&amp;#039; часть VIP. Сборщик мусора считает, что память, занимаемая объектом (экземпляром), может быть освобождена, когда исчезнут все ссылки на объект. И это почти единственное правило, определяющее время жизни объекта (экземпляра).&lt;br /&gt;
Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(),&lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза().&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь указатель на экземпляр класса &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; сохраняется в переменной &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039;. Поэтому, пока клоз &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; выполняется, живет и доступен через указатель &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039; и экземпляр &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039;.&lt;br /&gt;
По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только в пределах клоза. Следовательно, по завершении выполнения клоза &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; объект&amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; считается недоступным и может умереть. Если, конечно, он не породил ссылок на себя самого в какой-нибудь постоянной памяти во время вызова конструктора или других своих предикатов.&lt;br /&gt;
Случай&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
не меняет сути - пока выполняется предикат &amp;#039;&amp;#039;вызовПредикатаДругогоКлоза(МойОбъект)&amp;#039;&amp;#039; экземпляр продолжает жить и умрет после завершения выполнения предиката &amp;#039;&amp;#039;последнийПредикатКлоза(...)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
В большинстве случаев для построения более или менее полезной и хорошо организованной программы этого недостаточно. Объект, содержащий данные, используемые другими объектами, должен жить, пока это необходимо и должен быть доступен.&lt;br /&gt;
&lt;br /&gt;
Для этой цели используется хранение указателей на объекты в фактах. В VIP как предикаты, так и факты типизированы. А это значит, что при декларировании факта (а равно и предиката) необходимо указание домена - либо предопределённого в языке, либо объявленного в программе.&lt;br /&gt;
Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс.&lt;br /&gt;
&lt;br /&gt;
Поэтому имя интерфейса используется в качестве домена при декларировании фактов для хранения указателей или их передачи в качестве параметров.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;class мойКласс:мойИнтерфейс&lt;br /&gt;
end class мойКласс&amp;lt;/vip&amp;gt;&lt;br /&gt;
уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену &amp;#039;&amp;#039;мойИнтерфейс&amp;#039;&amp;#039;.&lt;br /&gt;
Поэтому объявление факта либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_F:(string УдобноеИмяЭкземпляра,мойИнтерфейс УказательНаЭкземпляр).&amp;lt;/vip&amp;gt;&lt;br /&gt;
либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
Являются правильными (не путать с целесообразностью) объявлениями.&lt;br /&gt;
Тогда в примере&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     мойУказатель_V:=МойОбъект,&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
время жизни объекта продлевается за пределы времени жизни клоза. Объект будет существовать все то время, пока на него существует указатель.&lt;br /&gt;
Когда объект больше не нужен, достаточно удалить ссылку на него, например так:&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:=erroneous,&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
или так&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     retract(мойУказатель_F(&amp;quot;ПочтовыеАдреса&amp;quot;,_УказательНаЭкземпляр)),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом следует иметь в виду, что смерть объекта ведет к смерти всех объектов, на которые данный объект хранит ссылки.&lt;br /&gt;
&lt;br /&gt;
Тогда, например, если имеется дерево, построенное на основе ссылок на объекты, то потеря указателя на корень дерева ведет к смерти всех объектов, входящих в дерево.&lt;br /&gt;
&lt;br /&gt;
Следует, однако, иметь в виду некоторые исключения из этого правила:&lt;br /&gt;
Как показал опыт, если объект хранит указатель на окно (домен &amp;#039;&amp;#039;vpiDomains::windowHandle&amp;#039;&amp;#039;), то сборщик мусора будет рассматривать этот объект, как используемый.&lt;br /&gt;
&lt;br /&gt;
То есть физически окно может быть закрыто, но не удаление указателя на это окно оставит объект в памяти. Аналогично часто указатели на COM-объекты могут быть причиной не освобождения памяти сборщиком мусора.&lt;br /&gt;
&lt;br /&gt;
Существует один замечательный предопределённый интерфейс, который, как и все интерфейсы является и доменом. Это интерфейс - object.&lt;br /&gt;
&lt;br /&gt;
Все экземпляры, принадлежащие доменам своих интерфейсов, в то же время являются дочерними доменами домена object.&lt;br /&gt;
Это значит, что практически возможно вместо&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
объявлять&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:object:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом будет компилироваться и работать&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     мойУказатель_V:=мойОбъект::new(),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Но вот вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
в этом случае не пропустит компилятор.&lt;br /&gt;
Вместо этого необходимо производить конвертацию&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     МойУказатель=convert(мойИнтерфейс,мойУказатель_V),&lt;br /&gt;
     МойУказатель:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы.&lt;br /&gt;
Сборщик мусора можно подтолкнуть предикатом&lt;br /&gt;
&amp;lt;vip&amp;gt;memory::garbageCollect()&amp;lt;/vip&amp;gt; но обычно в этом нет никакой необходимости.&lt;br /&gt;
&lt;br /&gt;
Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат finalize (если он есть), не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой. Также нельзя делать никаких предположений о порядке освобождения объектов и, соответственно, о порядке вызова finalize для разных объектов. Рекомендуется предикат finalize использовать исключительно для освобождения ресурсов, при этом объектные факты освобождать нет никакой необходимости. Раз уж объект удаляется, то и все его факты постигнет аналогичная участь.&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1844</id>
		<title>Жизненный цикл объекта</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%96%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0&amp;diff=1844"/>
		<updated>2007-12-18T21:52:45Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: Гибель объекта&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Создание объекта===&lt;br /&gt;
Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new().&lt;br /&gt;
&lt;br /&gt;
===Гибель объекта===&lt;br /&gt;
VIP не содержит никаких средств для уничтожения объектов. Тем не менее они не живут вечно (до конца жизни приложения). Время жизни объектов определяется сборщиком мусора (&amp;#039;&amp;#039;GarbageCollector&amp;#039;&amp;#039;), встроенного в &amp;#039;&amp;#039;RunTime&amp;#039;&amp;#039; часть VIP. Сборщик мусора считает, что память, занимаемая объектом (экземпляром), может быть освобождена, когда исчезнут все ссылки на объект. И это почти единственное правило, определяющее время жизни объекта (экземпляра).&lt;br /&gt;
Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(),&lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза().&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь указатель на экземпляр класса &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; сохраняется в переменной &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039;. Поэтому, пока клоз &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; выполняется, живет и доступен через указатель &amp;#039;&amp;#039;МойОбъект&amp;#039;&amp;#039; и экземпляр &amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039;.&lt;br /&gt;
По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только в пределах клоза. Следовательно, по завершении выполнения клоза &amp;#039;&amp;#039;мойПример&amp;#039;&amp;#039; объект&amp;#039;&amp;#039;мойОбъект&amp;#039;&amp;#039; считается недоступным и может умереть. Если, конечно, он не породил ссылок на себя самого в какой-нибудь постоянной памяти во время вызова конструктора или других своих предикатов.&lt;br /&gt;
Случай&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
не меняет сути - пока выполняется предикат &amp;#039;&amp;#039;вызовПредикатаДругогоКлоза(МойОбъект)&amp;#039;&amp;#039; экземпляр продолжает жить и умрет после завершения выполнения предиката &amp;#039;&amp;#039;последнийПредикатКлоза(...)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
В большинстве случаев для построения более или менее полезной и хорошо организованнй программы этого недостаточно. Объект, содержащий данные, используемые другими объектами, должен жить, пока это необходимо и должет быть доступен.&lt;br /&gt;
&lt;br /&gt;
Для этой цели используется хранение указателей на объекты в фактах. В VIP как предикаты, так и факты типизированы. А это значит, что при декларировании факта (а равно и предиката) необходимо указание домена - либо предопределённого в языке, либо объявленного в программе.&lt;br /&gt;
Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс.&lt;br /&gt;
&lt;br /&gt;
Поэтому имя интерфейса используется в качестве домена при декларировании фактов для хранения указателей или их передачи в качестве параметров.&lt;br /&gt;
Например,&lt;br /&gt;
&amp;lt;vip&amp;gt;class мойКласс:мойИнтерфейс&lt;br /&gt;
end classмойКласс&amp;lt;/vip&amp;gt;&lt;br /&gt;
уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену &amp;#039;&amp;#039;мойИнтерфейс&amp;#039;&amp;#039;.&lt;br /&gt;
Поэтому объявление факта либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_F:(string УдобноеИмяЭкземпляра,мойИнтерфейс УказательНаЭкземпляр).&amp;lt;/vip&amp;gt;&lt;br /&gt;
либо в виде&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
Являются правильными (не путать с целесообразностью) объявлениями.&lt;br /&gt;
Тогда в примере&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     МойОбъект=мойОбъект::new(),&lt;br /&gt;
     МойОбъект:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     мойУказатель_V:=МойОбъект,&lt;br /&gt;
     ...&lt;br /&gt;
     вызовПредикатаДругогоКлоза(МойОбъект),    &lt;br /&gt;
     ...&lt;br /&gt;
     последнийПредикатКлоза(...).&amp;lt;/vip&amp;gt;&lt;br /&gt;
время жизни объекта продлевается за пределы времени жизни клоза. Объект будет существовать все то время, пока на него существует указатель.&lt;br /&gt;
Когда объект больше не нужен, достаточно удалить ссылку на него, например так:&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:=erroneous,&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
или так&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     retract(мойУказатель_F(&amp;quot;ПочтовыеАдреса&amp;quot;,_УказательНаЭкземпляр)),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом следует иметь в виду, что смерть объекта ведет к смерти всех объектов, на которые данный объект хранит ссылки.&lt;br /&gt;
&lt;br /&gt;
Тогда, например, если имеется дерево, построенное на основе ссылок на объекты, то потеря указателя на корень дерева ведет к смерти всех объектов, входящих в дерево.&lt;br /&gt;
&lt;br /&gt;
Следует, однако, иметь в виду некоторые исключения из этого правила:&lt;br /&gt;
Как показал опыт, если объект хранит указатель на окно (домен &amp;#039;&amp;#039;vpiDomains::windowHandle&amp;#039;&amp;#039;), то сборщик мусора будет рассматривать этот объект, как используемый.&lt;br /&gt;
&lt;br /&gt;
То есть физически окно может быть закрыто, но не удаление указателя на это окно оставит объект в памяти. Аналогично часто указатели на COM-объекты могут быть причиной не освобождения памяти сборщиком мусора.&lt;br /&gt;
&lt;br /&gt;
Существует один замечательный предопределенный интерфейс, который, как и все интерфейсы является и доменом. Это интерфейс - object.&lt;br /&gt;
&lt;br /&gt;
Все экземпляры, принадлежащие доменам своих интерфейсов, в то же время являются дочерними доменами домена object.&lt;br /&gt;
Это значит, что практически возможно вместо&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:мойИнтерфейс:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
объявлять&lt;br /&gt;
&amp;lt;vip&amp;gt;facts&lt;br /&gt;
мойУказатель_V:object:=erroneous.&amp;lt;/vip&amp;gt;&lt;br /&gt;
При этом будет компилироваться и работать&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
  мойПример():-&lt;br /&gt;
     мойУказатель_V:=мойОбъект::new(),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Но вот вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     мойУказатель_V:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
в этом случае не пропустит компилятор.&lt;br /&gt;
Вместо этого необходимо прoизводить конвертацию&lt;br /&gt;
&amp;lt;vip&amp;gt;     ...&lt;br /&gt;
     МойУказатель=convert(мойИнтерфейс,мойУказатель_V),&lt;br /&gt;
     МойУказатель:мойПредикатИзМойОбъект(...),&lt;br /&gt;
     ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы.&lt;br /&gt;
Сборщик мусора можно подтолкнуть предикатом&lt;br /&gt;
&amp;lt;vip&amp;gt;memory::garbageCollect()&amp;lt;/vip&amp;gt; но обычно в этом нет никакой необходимости.&lt;br /&gt;
&lt;br /&gt;
Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат finalize (если он есть), не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой. Также нельзя делать никаких предположений о порядке освобождения объектов и, соответственно, о порядке вызова finalize для разных объектов. Рекомендуется предикат finalize использовать исключительно для освобождения ресурсов, при этом объектные факты освобождать нет никакой необходимости. Раз уж объект удаляется, то и все его факты постигнет аналогичная участь.&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1593</id>
		<title>Списки и рекурсия</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1593"/>
		<updated>2007-11-23T11:03:33Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: очепятки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Обработка списков как обработка последовательности элементов - это мощная техника, используемая в Прологе. В этом руководстве мы рассматриваем что такое списки, как их объявлять, и затем приводим несколько примеров, показывающих как использовать работу со списками в приложениях. Мы также определяем два хорошо известных предиката Пролога – member (член, элемент) и append (добавить) – рассматривая обработку списков как с рекурсивной, так и процедурной точки зрения.&lt;br /&gt;
&lt;br /&gt;
Затем мы представляем предикат findall - стандартный предикат языка системы Visual Prolog, позволяющий собирать решения в единую цель. В завершение этого руководства мы обсуждаем составные списки – комбинции различных типов элементов и, кроме того, - пример разбора с помощью разностных списков.&lt;br /&gt;
&lt;br /&gt;
==Что такое Список?==&lt;br /&gt;
&lt;br /&gt;
В Прологе &amp;#039;&amp;#039;список (list)&amp;#039;&amp;#039; является объектом, содержащим внутри произвольное число других объектов. Списки соответствуют, грубо говоря, массивам в других языках, но, в отличие от массивов, список не трубует декларирования его размера до начала его использования.&lt;br /&gt;
&lt;br /&gt;
Список, содержащий числа 1, 2 и 3 записывается как &lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 3 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порядок элементов в этом списке значим:&lt;br /&gt;
*Число &amp;quot;1&amp;quot; является первым элементом,&lt;br /&gt;
*&amp;quot;2&amp;quot; - второй,&lt;br /&gt;
*&amp;quot;3&amp;quot; - третий.&lt;br /&gt;
&lt;br /&gt;
Список [ 1, 2, 3 ] и список [ 1, 3, 2 ] различны.&lt;br /&gt;
&lt;br /&gt;
Каждый компонент списка называется элемент (element). Для того, чтобы сформировать списковую структуру данных, следует разделять элементы запятыми и заключать их всех в квадратные скобки. Посмотрим на некоторые примеры:&lt;br /&gt;
&amp;lt;vip&amp;gt;[&amp;quot;dog&amp;quot;, &amp;quot;cat&amp;quot;, &amp;quot;canary&amp;quot;]&lt;br /&gt;
[&amp;quot;valerie ann&amp;quot;, &amp;quot;jennifer caitlin&amp;quot;, &amp;quot;benjamin thomas&amp;quot;]&amp;lt;/vip&amp;gt;&lt;br /&gt;
Один и тот же элемент может быть представлен в списке несколько раз, например:&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 1, 3, 1 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Объявление Списков===&lt;br /&gt;
Для объявления домена - списка целых используется декларация домена, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Звездочка означает &amp;quot;список этого&amp;quot;; то есть, integer* означает &amp;quot;список целых&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на то, что слово &amp;#039;&amp;#039;&amp;quot;list&amp;quot;&amp;#039;&amp;#039; не имеет специального значения в Visual Prolog. Вы равным образом могли бы назвать Ваш списковый домен как &amp;#039;&amp;#039;zanzibar&amp;#039;&amp;#039;. Именно звездочка, а не имя, предписывает этому домену быть списком.&lt;br /&gt;
&lt;br /&gt;
Элементами в списке может быть что угодно, включая другие списки. Но все элементы в списке должны принадлежать одному домену, и дополнительно к декларации спискового домена должна быть декларация &amp;#039;&amp;#039;&amp;#039;domains&amp;#039;&amp;#039;&amp;#039; для элементов:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;#039;&amp;#039;elements&amp;#039;&amp;#039; должны быть приравнены к простым доменным типам (например, integer, real или symbol) или к набору возможных альтернатив, обозначенных различными функторами. Visual Prolog не допускает смешивание стандартных типов в списке. Например, следующие декларации ошибочно представляют списки, созданные из integers, reals и symbols:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    integer;&lt;br /&gt;
    real;&lt;br /&gt;
    symbol.&lt;br /&gt;
        /* Неправильно */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходом для объявления списков из integer, real и symbols является объявление домена общего для всех типов, где функтор показывает какому типу принадлежит тот или иной элемент. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    i(integer);&lt;br /&gt;
    r(real);&lt;br /&gt;
    s(symbol).&lt;br /&gt;
        /* функторами являются i, r и s */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Подробнее об этом - в этом же руководстве в разделе &amp;quot;Составные списки&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
====Головы и Хвосты====&lt;br /&gt;
&lt;br /&gt;
Список на самом деле является рекурсивным составным объетом. Он состоит из двух частей - головы списка, которым является первый элемент, и хвоста - списка, который включает все следующие элементы.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Хвост списка всегда есть список; голова списка есть элемент.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Например,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;голова списка [a, b, c] есть a&lt;br /&gt;
хвост списка [a, b, c] есть [b, c]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Что происходит, когда мы имеем дело со списком, содержащим один элемент? Ответом является:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;головой списка [c] является c&lt;br /&gt;
хвостом списка [c] является []&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Если многократно отнимать первый элемент от хвоста списка, мы получим в конечном итоге пустой список (&amp;#039;&amp;#039;&amp;#039;[ ]&amp;#039;&amp;#039;&amp;#039;).&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Пустой список не может быть разбит на голову и хвост.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Это означает, что, концептуально говоря, списки имеют древовидную структуру подобно другим составным объектам. Древовидная структура списка [a, b, c, d] есть:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a    list&lt;br /&gt;
      /    \&lt;br /&gt;
     b    list&lt;br /&gt;
         /    \&lt;br /&gt;
        c    list&lt;br /&gt;
            /    \&lt;br /&gt;
           d      []&amp;lt;/pre&amp;gt;&lt;br /&gt;
Более того, одноэлементный список, такой как [a] - это не тот же самый элемент, который этот список содержит, поскольку [a] является действительно составной структурой данных, как это видно здесь:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a     []&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Представления Списков==&lt;br /&gt;
&lt;br /&gt;
Пролог содержит метод для явного обозначения головы и хвоста списка. Вместо разделения элементов запятыми можно отделять голову от хвоста вертикальной чертой (|). Например,&lt;br /&gt;
&lt;br /&gt;
[a, b, c] эквивалентно [a|[b, c]]&lt;br /&gt;
&lt;br /&gt;
и, продолжая процесс,&lt;br /&gt;
&lt;br /&gt;
[a|[b,c]] эквивалентно [a|[b|[c]]],&lt;br /&gt;
&lt;br /&gt;
что эквивалентно [a|[b|[c|[]]]]&lt;br /&gt;
&lt;br /&gt;
Можно даже использовать оба способа разделения в одном и том же списке, рассматривая вертикальную черту как разделитель самого низкого уровня. Следовательно, можно записать [a, b, c, d] как [a, b|[c, d]]. Таблица 1 дает дополнительные примеры.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 1: Головы и Хвосты списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список&lt;br /&gt;
!Голова&lt;br /&gt;
!Хвост&lt;br /&gt;
|-&lt;br /&gt;
|[&amp;#039;a&amp;#039;, &amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[&amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|-&lt;br /&gt;
|[ &amp;#039;a&amp;#039; ]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[] &lt;br /&gt;
|-&lt;br /&gt;
|/*пустой список*/ [ ]&lt;br /&gt;
|неопределен&lt;br /&gt;
|неопределен&lt;br /&gt;
|-&lt;br /&gt;
|[[1, 2, 3], [2, 3, 4], []]&lt;br /&gt;
|[1, 2, 3]&lt;br /&gt;
|[[2, 3, 4], []]&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
В Таблице 2 приведены некоторые примеры унификации списков.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 2: Унификация Списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список 1&lt;br /&gt;
!Список 2&lt;br /&gt;
!Связывание Переменных&lt;br /&gt;
|-&lt;br /&gt;
|[X, Y, Z]&lt;br /&gt;
|[эгберт, ест, мороженое]&lt;br /&gt;
|X=эгберт, Y=ест, Z=мороженое&lt;br /&gt;
|-&lt;br /&gt;
|[7]&lt;br /&gt;
|[X &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Y] &lt;br /&gt;
|X=7, Y=[]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2, 3, 4]&lt;br /&gt;
|[X, Y &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Z]&lt;br /&gt;
|X=1, Y=2, Z=[3,4]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2]&lt;br /&gt;
|[3 &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; X]&lt;br /&gt;
|fail&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Использование Списков==&lt;br /&gt;
&lt;br /&gt;
Поскольку списки являются в действительности рекурсивными составными структурами данных, для их обработки необходимы и рекурсивные алгоритмы. Самый естественный способ обработки списков - сквозной просмотр, в ходе которого что-то делается с каждым элементом, до тех пор, пока не достигнут конец.&lt;br /&gt;
&lt;br /&gt;
Как правило, такого рода алгоритмы используют два клауза. Один из них говорит о том, как поступать с обыкновенным списком, который может быть разделен на голову и хвост. Другой говорит о том, что делать с пустым списком.&lt;br /&gt;
&lt;br /&gt;
===Вывод Списков на печать===&lt;br /&gt;
&lt;br /&gt;
Например, если Вы хотите только вывести на печать элементы списка, то вот что Вы делаете:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  write_a_list : (integer*).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  write_a_list([]). /* Если список пустой, ничего не делаем. */&lt;br /&gt;
  write_a_list([H|T]):- /* Сопоставляем голову с H и хвост с T, и... */&lt;br /&gt;
    stdio::write(H),stdio::nl, /*выводим H и переводим строку*/&lt;br /&gt;
    write_a_list(T).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь мы видим два клауза write_a_list, которые можно выразить но обычном языке:&lt;br /&gt;
&lt;br /&gt;
*Для вывода на печать пустого списка ничего не надо делать.&lt;br /&gt;
*Иначе, для вывода на печать списка, вывести на печать его голову (она есть просто элемент), и потом вывести на печать хвост списка (он, как известно, есть список).&lt;br /&gt;
&lt;br /&gt;
Первый раз, когда вызывается:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такой вызов сопоставляется со вторым клаузом, с головой H=1 и T=[2, 3]. Это приводит к выводу на печать 1, затем рекурсивно вызывается write_a_list с аргументом в виде хвоста списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([2, 3]).&lt;br /&gt;
  /* Это вызов write_a_list(T). */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот второй вызов опять сопоставляется со вторым клаузом, где, на этот раз H=2 и T=[3], поэтому выводится 2 и опять рекурсивно вызывается write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С каким клаузом теперь такой вызов сопоставлятся? Напомним, что, хотя список [3] имеет всего один элемент, у него есть голова и хвост - голова есть 3, а хвост есть []. Таким образом, этот вызов опять сопоставляется со вторым клаузом с H=3 и T=[]. Теперь выводится 3 и  вызывается рекурсивно write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь становится понятно для чего нужен первый клауз. Второй клауз не может быть сопоставлен с таким вызовом, поскольку [] не может быть разделен на голову и хвост. Если бы первого клазуа здесь не было бы, то выполнение goal оказалось бы неуспешным. Но, поскольку он есть, то первый клауз сопоставляется с вызовом и выполнение goal успешно завершается и нечего более не делается.&lt;br /&gt;
&lt;br /&gt;
===Подсчет элементов в Списке===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь, как подсчитать число элементов в списке, или какова длина списка? Логично определить:&lt;br /&gt;
&lt;br /&gt;
*Длина пустого списка [] есть 0.&amp;lt;br /&amp;gt;&lt;br /&gt;
*Длина любого другого списка есть 1 плюс длина его хвоста.&lt;br /&gt;
&lt;br /&gt;
Можно ли это запрограммировать? На Прологе это очень просто. Всего два клауза:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], 0).&lt;br /&gt;
  length_of([_|T], L):-&lt;br /&gt;
    length_of(T, TailLength),&lt;br /&gt;
    L = TailLength + 1.&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L),&lt;br /&gt;
  stdio::write(L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотрите прежде всего на второй клауз. Строго говоря, [_|T] сопоставляется с любым непустым списком, связывая T с хвостом списка. Значение головы неважно, если она есть, она может быть учтена как один элемент.&lt;br /&gt;
&lt;br /&gt;
Тогда вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется со вторым клаузом, с T=[2, 3]. Следующим шагом является вычисление длины хвоста T. Когда это сделано (не имеет значение, как), TailLength получит значение 2, и компьютер теперь может добавить 1 к ней и связать L со значением 3. Как выполняется этот промежуточный шаг? Надо найти длину списка [2, 3], путем удовлетворения цели&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([2, 3], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Другими словами, length_of вызывает себя рекурсивно. Этот вызов сопоставляется со вторым клаузом, связывая&lt;br /&gt;
&lt;br /&gt;
*[3] и T в вызове клаузы и&lt;br /&gt;
*TailLength с L в клаузе.&lt;br /&gt;
&lt;br /&gt;
Подчеркиваем, TailLength в вызове никак не пересекается с TailLength в клаузе, поскольку &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;каждый рекурсивный вызов клауза имеет собственный набор переменных&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Итак, теперь задача - найти длину списка [3], которая есть 1, и мы добавляем 1 к этому значению, чтобы получить длину списка [2, 3], что будет 2. Ну и хорошо!.&lt;br /&gt;
&lt;br /&gt;
Аналогично, length_of вызывает себя рекурсивно опять для получения длины списка [3]. Хвост  [3] есть [], поэтому T связывается с [], и задача теперь - получение длины списка [] и добавление к ней 1, что дает длину списка [3].&lt;br /&gt;
&lt;br /&gt;
Теперь все просто. Цель:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется с &amp;#039;&amp;#039;первым&amp;#039;&amp;#039; клаузом, связывая TailLength с 0. Поэтому теперь компьютер может добавить 1 к нему, получая длину списка [3], и возвращаясь теперь в вызывавший клауз. Это, в свою очередь, опять добавляет 1, давая длину списка [2, 3], и возвращается в клауз, который его вызывал; этот первоначальный клауз добавит снова 1, давая длину списка [1, 2, 3].&lt;br /&gt;
&lt;br /&gt;
Не растерялись? Мы надеемся, нет. В следующей короткой иллюстрации мы сводим воедино все вызовы. Мы использовали здесь прием подстрочника для того, чтобы показать, что аналогично называемые переменные в разных клаузах или различные вызовы того же самого клауза - одно и то же. &lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L1).&lt;br /&gt;
my::length_of([2, 3], L2).&lt;br /&gt;
my::length_of([3], L3).&lt;br /&gt;
my::length_of([], 0).&lt;br /&gt;
L3 =  0+1 = 1.&lt;br /&gt;
L2 = L3+1 = 2.&lt;br /&gt;
L1 = L2+1 = 3.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что Вам не нужно каждый раз создавать такого рода предикаты самостоятельно, Вы можете использовать готовый предикат &amp;#039;&amp;#039;&amp;#039;list::length&amp;#039;&amp;#039;&amp;#039; из PFC.&lt;br /&gt;
&lt;br /&gt;
===Хвостовая рекурсия===&lt;br /&gt;
&lt;br /&gt;
Вы, очевидно, заметили, что length_of не является (и не может быть) предикатом с хвостовой рекурсией, поскольку рекурсивный вызов не является последним шагом в его клаузе. Возможно ли создать предикат, определяющий длину, так, чтобы он был предикатом с хвостовой рекурсией? Да, но это потребует некоторых усилий.&lt;br /&gt;
&lt;br /&gt;
Проблема с предикатом length_of в том, что длину списка нельзя вычислить до тех пор, пока не вычислена длина его хвоста. Но из этой ситуации есть выход. Нам потребуется предикат, вычисляющий длину списка, с тремя аргументами.&lt;br /&gt;
&lt;br /&gt;
*Один из них - это список, от которого компьютер будет откусывать по одному элементу на каждом вызове до тех пор, пока этот список, как и прежде, не превратится в пустой список.&lt;br /&gt;
*Второй - это свободный аргумент, который в конечном итоге вернет результат (длину).&lt;br /&gt;
*Третий - это счетчик, значение которого начинается с нуля и увеличивается с каждым вызовом.&lt;br /&gt;
&lt;br /&gt;
Когда список в конечном итоге станет пустым, мы проунифицируем счетчик с несвязанным результатом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer, integer) procedure(i,o,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], Result, Result).&lt;br /&gt;
  length_of([_|T], Result, Counter):-&lt;br /&gt;
    NewCounter = Counter + 1,&lt;br /&gt;
    length_of(T, Result, NewCounter).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L, 0), /* Начинаем со счетчиком Counter = 0 */&lt;br /&gt;
  stdio::write(&amp;quot; L = &amp;quot;, L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта версия предиката length_of более сложная и во многих смыслах менее логичная, чем предыдущая. Мы ее представили здесь главным образом для того, чтобы показать, что на практике &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;вы можете часто построить алгоритм с хвостовой рекурсией для задач, которые на первый взгляд требуют рекурсии другого типа&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
===Модификация Списка===&lt;br /&gt;
&lt;br /&gt;
Иногда требуется создать другой список из заданного списка. Это делается путем просмотра списка, элемент за элементом, заменяя каждый элемент вычисленным значением. Например, как эта программа, которая добавляет 1 к каждому элементу исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  add1 : (integer*, integer*) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  add1([], [])./* граничное условие */&lt;br /&gt;
  add1([Head|Tail],[Head1|Tail1]):- /* отделяем голову от остального списка*/&lt;br /&gt;
    Head1 = Head+1, /* добавляем 1 к элементу-голове */&lt;br /&gt;
    add1(Tail, Tail1)./* далаем это с остальной частью списка*/&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::add1([1,2,3,4], NewList),&lt;br /&gt;
  stdio::write(NewList)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На обычном языке это звучит так:&lt;br /&gt;
*Добавление 1 ко всем элементам пустого списка порождаем пустой список,&lt;br /&gt;
*Для добавления 1 ко всем элемента любого другого списка:&lt;br /&gt;
**добавить 1 к голове и сделать эту голову головой результирующего списка, а затем&lt;br /&gt;
**добавить 1 к каждому элемента хвоста и этот хвост сделать хвостом результата.&lt;br /&gt;
&lt;br /&gt;
Загрузим программу и выполним такую цель&lt;br /&gt;
&amp;lt;vip&amp;gt;add1([1,2,3,4], NewList).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель вернет&lt;br /&gt;
&amp;lt;vip&amp;gt;NewList=[2,3,4,5]&lt;br /&gt;
1 Solution&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Опять о хвостовой рекурсии===&lt;br /&gt;
&lt;br /&gt;
Является ли предикат add1 проедикатом с хвостовой рекурсией? &lt;br /&gt;
Если у Вас есть опыт использования Lisp или Pascal, Вы могли бы подумать, что нет, поскольку Вы бы рассуждали так:&lt;br /&gt;
&lt;br /&gt;
*Делим список на &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;.&lt;br /&gt;
*Добавляем 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавляя 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
*Соединяем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, что дает результирующий список.&lt;br /&gt;
&lt;br /&gt;
Это не похоже на хвостовую рекурсию, поскольку последний шаг - не рекурсивный вызов.&lt;br /&gt;
&lt;br /&gt;
Однако, и это важно, – &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Это не то, что делает Пролог&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;. В Visual Prolog add1 является предикатом с хвостовой рекурсией, поскольку выполняется в действительности следующим образом:&lt;br /&gt;
&lt;br /&gt;
*Связать голову и хвост исходного списка с &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, соответственно.&lt;br /&gt;
*Связать голову и хвост результирующего списка с &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, соответственно. (&amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; пока не получили значений.)&lt;br /&gt;
*Добавить 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавить 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Когда это сделано, &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;уже являются&amp;#039;&amp;#039;&amp;#039; головой и списком результата и отдельной операции по их соединению нет. Поэтому рекурсивный вызов и является последним шагом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h6&amp;gt;Снова Модификация Списков&amp;lt;/h6&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Конечно, не всегда модификации подлежит каждый элемент. Посмотрим на программу, которая сканирует список чисел и копирует его, удаляя отрицательные числа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  discard_negatives : (integer*, integer*) procedure(i,o). /*удалить отрицательные*/&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  discard_negatives([], []).&lt;br /&gt;
  discard_negatives([H|T], ProcessedTail):-&lt;br /&gt;
    H &amp;lt; 0,&lt;br /&gt;
    !, /* Если H отрицательно, пропускаем его */&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
  discard_negatives([H|T], [H|ProcessedTail]):-&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::discard_negatives ([2, -45, 3, 468], X),&lt;br /&gt;
  stdio::write(X).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напрмер, цель&lt;br /&gt;
&amp;lt;vip&amp;gt;my::discard_negatives([2, -45, 3, 468], X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
дает&lt;br /&gt;
&amp;lt;vip&amp;gt;X=[2, 3, 468].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
А вот - предикат который копирует элементы списка, добавляя для каждого элемента его дубликат:&lt;br /&gt;
&amp;lt;vip&amp;gt;doubletalk([], []).&lt;br /&gt;
doubletalk([H|T], [H, H|DoubledTail]) :-&lt;br /&gt;
  doubletalk(T, DoubledTail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Принадлежнось списку===&lt;br /&gt;
&lt;br /&gt;
Допустим, имеется список с именами &amp;#039;&amp;#039;John&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Leonard&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Eric&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Frank&amp;#039;&amp;#039; и требуется, используя Visual Prolog, выяснить, принадлежит ли заданное имя этому списку. Другими словами, надо определить &amp;quot;отношение&amp;quot; между двумя аргументами: именем и списком имен. Это соответствует предикату&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember : (name, name*).&lt;br /&gt;
  /* &amp;quot;name&amp;quot; принадлежит списку &amp;quot;name*&amp;quot; */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В программе e01.pro первый клауз исследует голову списка. Если голова списка совпадает с искомым именем, то можно сделать заключение, что Name принадлежит списку. Поскольку хвост списка нас не интересует, то это мы представляем анонимной переменной. Благодаря первому клаузу, цель&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;])&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
удовлетворена.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Программа e01.pro */&lt;br /&gt;
class my&lt;br /&gt;
predicates&lt;br /&gt;
  isMember : (A, A*) determ.&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  isMember(Name, [Name|_]) :-&lt;br /&gt;
    !.&lt;br /&gt;
  isMember(Name, [_|Tail]):-&lt;br /&gt;
    isMember(Name,Tail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;]),&lt;br /&gt;
  !,&lt;br /&gt;
  stdio::write(&amp;quot;Success&amp;quot;)&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если голова списка не есть Name, то надо исследовать, не содержится ли Name в хвосте списка.&lt;br /&gt;
&lt;br /&gt;
На обычном языке:&lt;br /&gt;
&lt;br /&gt;
Name принадлежит списку, если Name является первым элементом списка, или&amp;lt;br /&amp;gt;&lt;br /&gt;
Name принадлежит списку, если Name принадлежит хвосту.&lt;br /&gt;
&lt;br /&gt;
Второй клауз предиката isMember относится к этому отношению. Таким образом на Visual Prolog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember(Name, [_|Tail]) :-&lt;br /&gt;
    isMember(Name, Tail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Добавление списка к другому списку: декларативное и рекурсивное решения===&lt;br /&gt;
&lt;br /&gt;
Рассмотренный предикат member программы e01.pro работает в двух направлениях. Вернемся к его клаузам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
member(Name, [Name|_]).&lt;br /&gt;
member(Name, [_|Tail]) :-&lt;br /&gt;
  member(Name, Tail).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На эти клаузы можно смотреть с двух различных точек зрения: декларативной и процедурной.&lt;br /&gt;
&lt;br /&gt;
*С декларативной точки зрения, клаузы выражают: &lt;br /&gt;
**Name (Имя) принадлежит списку, если голова списка есть Name&lt;br /&gt;
**иначе Name принадлежит списку, если оно (Имя) принадлежит хвосту.&lt;br /&gt;
*С процедурной точки зрения, эти же два клауза могут быть интерпретированы так:&amp;lt;br/&amp;gt;Чтобы найти элемент списка &lt;br /&gt;
**Найдите его голову&lt;br /&gt;
**иначе найдите элемент хвоста списка.&lt;br /&gt;
&lt;br /&gt;
Эти две точки зрения соответствуют целям&lt;br /&gt;
&amp;lt;vip&amp;gt;member(2, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;member(X, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате первый вызов поручает Visual Prolog(у) проверить, истино ли нечто (принадлежность числа 2 списку [1,2,3,4]). Второй вызов поручает Visual Prolog(у) найти все члены списка [1,2,3,4]. Не смущайтесь этим. Предикат member является одним и тем же, но на его поведение можно смотреть под разными углами.&lt;br /&gt;
&lt;br /&gt;
====Рекурсия с процедуральной точки зрения====&lt;br /&gt;
&lt;br /&gt;
Прелесть Пролога заключается в том, что часто, когда мы конструируем клаузы для предиката, будучи на одной точке зрения, они будут работать и при взгляде с другой точки зрения. Чтобы обнаружить эту дуальность, мы приведем пример предиката для добавления (append) одного списка к другому. Мы определяем предикат append с тремя аргументами:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append(List1, List2, List3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот предикат интегрирует списки List1 и List2 в форму списка List3 так, что список List2 дописывается в конце списка List1. То есть содержательно - осуществляется добавление списка List2 к списку LIst1. Опять мы используем рекурсию (на этот раз с процедуральной точки зрения).&lt;br /&gt;
&lt;br /&gt;
Если список List1 пустой, результатом добавления списка List1 к списку List2 будет тот же самый List2. Запишем это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([], List2, List2).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если список List1 не пустой, то можно преобразовать списки List1 и List2 к форме списка &amp;#039;&amp;#039;List3&amp;#039;&amp;#039;, сделав голову списка List1 головой списка List3. В приведенном коде переменная H используется в качестве головы как списка List1, так и списка List3. хвост списка List3 есть список L3, который составлен из остатка списка List1 (а именно, L1) и всего списка List2. Опять выразим это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([H|L1], List2, [H|L3]) :-&lt;br /&gt;
   append(L1, List2, L3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append работает следующим образом: пока список List1 не пустой, рекурсивное правило дописывает один элемент каждый раз к списку List3. Когда список List1 становится пустым, первый клауз  clause обеспечивает дописывание списка List2 в конец списка List3.&lt;br /&gt;
&lt;br /&gt;
====Варианты использования одного предиката====&lt;br /&gt;
&lt;br /&gt;
Подходя к предикату append с декларативной точки зрения, мы определили его как отношение между тремя списками. Это отношение справедливо также, если списки List1 и List3 известны, а List2 - нет. Более того, это также работает, если только List3 известен. Например, для того, чтобы выяснить, какие два списка могли бы быть соединены для получения известного списка, можно использовать вызов в форме&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, L2, [1, 2, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С таким целевым вызовом, Visual Prolog найдет следующие решения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
L1=[], L2=[1,2,4]&lt;br /&gt;
L1=[1], L2=[2,4]&lt;br /&gt;
L1=[1,2], L2=[4]&lt;br /&gt;
L1=[1,2,4], L2=[]&lt;br /&gt;
4 Solutions&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно использовать предикат append для нахождения списка, который следовало бы добавить к списку [3,4] для получения списка[1,2,3,4]. Попробуем такой вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, [3,4], [1,2,3,4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Visual Prolog находит решение&lt;br /&gt;
&amp;lt;vip&amp;gt;L1=[1,2].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append определяет отношение между &amp;#039;&amp;#039;входным набором (input set)&amp;#039;&amp;#039; и &amp;#039;&amp;#039;выходным набором (output set)&amp;#039;&amp;#039; таким образом, что отношение применимо в обе стороны. При таком отношении возникает вопрос&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Что является выходным набором для заданного входного?&amp;#039;&amp;#039; или &amp;#039;&amp;#039;Какой входной набор соответствует заданному выходному?&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Статус аргументов данного вызова предиката известен как &amp;#039;&amp;#039;поток (или шаблон) ввода-вывода&amp;#039;&amp;#039;. Аргумент, который связан или наследуется в момент вызова является входным аргументом и обозначается как (i). Свободный аргумент является выходным аргументом и обозначается как (o).&lt;br /&gt;
&lt;br /&gt;
Предикат append обладает свойством поддерживать любой шаблон ввода-вывода, какой требуется. Однако не все предикаты имеют возможность вызова с различными шаблонами ввода-вывода. Когда клауз Пролога способен поддерживать множество шаблонов ввода-вывода, он называется инверсным клаузом. Целый набор предикатов для обработки списков содержится в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Все Решения Сразу==&lt;br /&gt;
&lt;br /&gt;
Откаты и рекурсии являются двумя способами выполнения повторяющихся процессов. Рекурсия предпочтительнее, поскольку, в отличие от отката, позволяет передать данные через аргументы от одного рекурсивного вызова к следующему. Благодаря этому, рекурсивные процедуры могут использовать промежуточные результаты или счетчики по ходу выполнения.&lt;br /&gt;
&lt;br /&gt;
Однако есть одна вещь, которую, в отличие от рекурсии, могут делать откаты, а именно – искать все альтернативные решения посредством одного вызова. Поэтому Вы можете оказаться в затруднительном положении: Вам нужно получить все решения за один вызов, и, при этом, они Вам нужны все сразу, как часть единой интегрированной структуры данных. Что делать?&lt;br /&gt;
&lt;br /&gt;
К счастью, Visual Prolog позволяет найти выход из этого положения. Предопределенная конструкция обработки списков получает целевой вызов в качестве своего аргумента и собирает все решения для этого вызова в единый выходной список. Такая конструкция для списков имеет два аргумента:&lt;br /&gt;
&lt;br /&gt;
*Первый аргумент, VarName, определяет аргумент в вызываемом целевом предикате, значения которого будут собираться в список.&lt;br /&gt;
*Второй - mypredicate - определяет предикат, который будет получать значения.&lt;br /&gt;
*Выходной параметр ListParam, является переменной, которая содержит список значений, полученных в ходе отката.&lt;br /&gt;
&lt;br /&gt;
Программа e02.pro использует обработку списка для вывода среднего возраста группы людей.&lt;br /&gt;
&amp;lt;vip&amp;gt;        /* Программа e02.pro */&lt;br /&gt;
class my&lt;br /&gt;
domains&lt;br /&gt;
  name = string.&lt;br /&gt;
  address = string.&lt;br /&gt;
  age = integer.&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  person : (name, address, age) nondeterm anyflow.&lt;br /&gt;
  sumlist : (age*, age, integer)procedure(i,o,o).&lt;br /&gt;
end class my&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  sumlist([],0,0).&lt;br /&gt;
  sumlist([H|T], Sum, N):-&lt;br /&gt;
    sumlist(T, S1, N1),&lt;br /&gt;
    Sum=H+S1, N=1+N1.&lt;br /&gt;
  &lt;br /&gt;
  person(&amp;quot;Sherlock Holmes&amp;quot;,&amp;quot;22B Baker Street&amp;quot;, 42).&lt;br /&gt;
  person(&amp;quot;Pete Spiers&amp;quot;,&amp;quot;Apt. 22, 21st Street&amp;quot;, 36).&lt;br /&gt;
  person(&amp;quot;Mary Darrow&amp;quot;,&amp;quot;Suite 2, Omega Home&amp;quot;, 51).&lt;br /&gt;
end implement my&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  L = [ Age || my::person(_, _, Age)],&lt;br /&gt;
  my::sumlist(L, Sum, N),&lt;br /&gt;
  Ave = Sum/N,&lt;br /&gt;
  stdio::write(&amp;quot;Average=&amp;quot;, Ave, &amp;quot;. &amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Клауз обработки списка в этой программе создает список L, который является списком всех возрастов, полученных от предиката person. Если бы потребовалось бы собрать список всех людей в возрасте 42 лет, можно было бы задать следующий цель-вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ Who || my::person(Who, _, 42) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий код порождает список всех положительных чисел исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ X || X = list::getMember_nd([2,-8,-3,6]), X &amp;gt; 0]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Смешанные списки==&lt;br /&gt;
&lt;br /&gt;
Список целых объявляется просто&lt;br /&gt;
&amp;lt;vip&amp;gt;integer*&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же верно и для списков вещественных чисел (integer), символьных (symbol) списков или списков строк (string).&lt;br /&gt;
&lt;br /&gt;
Однако часто приходится хранить комбинацию различных типов элементов в составе одного и того же списка, такую как:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[2, 3, 5.12, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot;].&lt;br /&gt;
        /* Не корректно для Visual Prolog*/&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Смешанные списки&amp;#039;&amp;#039; - списки, содержащие элементы более чем одного типа. Для работы со списками разнотипных элементов должны использоваться специальные объявления, поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;одному и тому же&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; домену. Способом создания списка, который хранит такие различные типы элементов является использование функторов, поскольку домен может представляться &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;более, чем одним&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; функтором, каждый со своими т&amp;#039;&amp;#039;&amp;#039;и&amp;#039;&amp;#039;&amp;#039;повыми аргументами.&lt;br /&gt;
&lt;br /&gt;
Пример объаявления списка, который может хранить целые, символы, строки или списки таких  даных:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  /* функторами являются l, i, c, and s */&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Список&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 2, 9, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot; ]&lt;br /&gt;
/* Не корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
на Visual Prolog следовало бы записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[i(2), i(9), l([s(&amp;quot;food&amp;quot;), s(&amp;quot;goo&amp;quot;)]), s(&amp;quot;new&amp;quot;)]&lt;br /&gt;
        /* Корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример предиката append показывает как использовать такого рода декларации в стандартных программах манипулирования списками.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  append : (A*,A*,A*) procedure (i,i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  append([], L, L).&lt;br /&gt;
  append([X|L1], L2, [X|L3]):-&lt;br /&gt;
    append(L1, L2, L3).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::s(&amp;quot;likes&amp;quot;),my::l([my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;mary&amp;quot;)])],&lt;br /&gt;
    [my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;sue&amp;quot;)], &lt;br /&gt;
    Ans&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Первый список: &amp;quot;, Ans,&amp;quot;\n\n&amp;quot;),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::l([my::s(&amp;quot;This&amp;quot;),my::s(&amp;quot;is&amp;quot;),my::s(&amp;quot;a&amp;quot;),my::s(&amp;quot;list&amp;quot;)]),my::s(&amp;quot;bee&amp;quot;)], &lt;br /&gt;
    [my::c(&amp;#039;c&amp;#039;)], &lt;br /&gt;
    Ans2&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Второй списко: &amp;quot;, Ans2, &amp;quot;\n\n&amp;amp;quot).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Разбор с использованием списков==&lt;br /&gt;
&lt;br /&gt;
Ниже приведена программа, демонстрирующая [[wikipedia:parsing|разбор]] (parsing)с использованием списков. Процесс разбора работает в методом сворачивания. В этом примере входная строка преобразуется в структуру данных Пролога, которая может быть использована далее.&lt;br /&gt;
&lt;br /&gt;
Этот разборщик предназначен для примитивного формального языка. Хотя этот пример достаточно сложен с точки зрения этого руководства, мы решили поместит его здесь, поскольку разбор является одной из областей, где Visual Prolog очень эффективен. Если Вы чувствуете себя не вполне готовым для этого раздела, Вы можете этот пример пропустить и продолжить чтение руководства без потери содержательности.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;#include @&amp;quot;pfc\exception\exception.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\string\string.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\console\console.ph&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class my_t&lt;br /&gt;
predicates&lt;br /&gt;
  tokl : (string, string*) procedure (i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_t&lt;br /&gt;
clauses&lt;br /&gt;
  tokl(Str, [H|T]) :-&lt;br /&gt;
    string::fronttoken(Str, H, Str1),&lt;br /&gt;
    !,&lt;br /&gt;
    tokl(Str1, T).&lt;br /&gt;
  tokl(_, []).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
/* * * * * * * * * * * * * * * * * * * *  *&lt;br /&gt;
* Вторая часть этой программы - разборщик *&lt;br /&gt;
* * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
class my_p&lt;br /&gt;
domains&lt;br /&gt;
  program = program(statement*).&lt;br /&gt;
  /* * * * * * * * * * * * * * * * * * * * *  * &lt;br /&gt;
  * Определение что есть языковая конструкция *&lt;br /&gt;
  * (предложение языка)                       *&lt;br /&gt;
  * * * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
  statement =&lt;br /&gt;
    if_Then_Else(exp, statement, statement);&lt;br /&gt;
    if_Then(exp, statement);&lt;br /&gt;
    while(exp, statement);&lt;br /&gt;
    assign(id, exp).&lt;br /&gt;
  /* * * * * * * * * * *  *&lt;br /&gt;
  * Определение выражения *&lt;br /&gt;
  * * * * * * * *  * * *  */&lt;br /&gt;
  exp = &lt;br /&gt;
    plus(exp, exp);&lt;br /&gt;
    minus(exp, exp);&lt;br /&gt;
    var(id);&lt;br /&gt;
    int(integer).&lt;br /&gt;
    id = string.&lt;br /&gt;
&lt;br /&gt;
  predicates&lt;br /&gt;
    s_program : (string*, program) procedure (i,o).&lt;br /&gt;
    s_statement : (string*, string*, statement) determ (i,o,o).&lt;br /&gt;
    s_statement_list : (string*, string*, statement*) determ (i,o,o).&lt;br /&gt;
    s_exp : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
    s_exp1 : (string*, string*, exp, exp) determ (i,o,i,o).&lt;br /&gt;
    s_exp2 : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_p&lt;br /&gt;
clauses&lt;br /&gt;
  s_program(TokenList, program(StatementList)):-&lt;br /&gt;
    s_statement_list(TokenList, _, StatementList),&lt;br /&gt;
    !.&lt;br /&gt;
  s_program(_, program([])).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement_list([], [], []) :- !.&lt;br /&gt;
  s_statement_list(List1, List4, [Statement|Program]) :-&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;;&amp;quot;|List3],&lt;br /&gt;
    s_statement_list(List3, List4, Program).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List7,if_then_else(Exp,Statement1, Statement2)):-&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement1),&lt;br /&gt;
    List4=[&amp;quot;else&amp;quot;|List5],!,&lt;br /&gt;
    s_statement(List5, List6, Statement2),&lt;br /&gt;
    List6=[&amp;quot;fi&amp;quot;|List7].&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List5,if_then(Exp, Statement)) :- !,&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement),&lt;br /&gt;
    List4=[&amp;quot;fi&amp;quot;|List5].&lt;br /&gt;
  s_statement([&amp;quot;do&amp;quot;|List1], List4,while(Exp, Statement)) :- !,&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;while&amp;quot;|List3],&lt;br /&gt;
    s_exp(List3, List4, Exp).&lt;br /&gt;
  s_statement([ID|List1], List3,assign(Id,Exp)) :-&lt;br /&gt;
    string::isname(ID),&lt;br /&gt;
    List1=[&amp;quot;=&amp;quot;|List2],&lt;br /&gt;
    s_exp(List2, List3, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp(List1, List3, Exp):-&lt;br /&gt;
    s_exp2(List1, List2, Exp1),&lt;br /&gt;
    s_exp1(List2, List3, Exp1, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp1([&amp;quot;+&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, plus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1([&amp;quot;-&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, minus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1(List, List, Exp, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp2([Int|Rest], Rest, int(I)) :-&lt;br /&gt;
    trap(I = toTerm(Int),Error,exception::clear_fail(Error)),&lt;br /&gt;
    !.&lt;br /&gt;
  s_exp2([Id|Rest], Rest, var(Id)) :-&lt;br /&gt;
    string::isname(Id).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  stdio::write(Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res),&lt;br /&gt;
  stdio::write(Res).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Загрузите программу и выполните целевой вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;goal&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Prolog вернет структуру программы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;Ans = [&amp;quot;b&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;if&amp;quot;,&amp;quot;b&amp;quot;,&amp;quot;then&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;1&amp;quot;,&lt;br /&gt;
    &amp;quot;else&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;fi&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;do&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;a&amp;quot;,&lt;br /&gt;
    &amp;quot;-&amp;quot;,&amp;quot;1&amp;quot;,&amp;quot;while&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;;&amp;quot;],&lt;br /&gt;
Res=program&lt;br /&gt;
  (&lt;br /&gt;
  [assign(&amp;quot;b&amp;quot;,int(2)),&lt;br /&gt;
   if_then_else&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;b&amp;quot;),&lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(1)), &lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(2))&lt;br /&gt;
     ),&lt;br /&gt;
   while&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;a&amp;quot;),&lt;br /&gt;
     assign&lt;br /&gt;
       (&lt;br /&gt;
       &amp;quot;a&amp;quot;,&lt;br /&gt;
       minus&lt;br /&gt;
         (&lt;br /&gt;
         var(&amp;quot;a&amp;quot;),&lt;br /&gt;
         int(1)&lt;br /&gt;
         )&lt;br /&gt;
       )&lt;br /&gt;
     )&lt;br /&gt;
  ])&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Преобразование в этом примере представлено двумя шагами: сканирование и разбор. Предикат tokl  - это сканер. Он принимает строку и преобразует ее в список токенов. Все предикаты с именами, начинающимися с s_ являются предикатами парсера. В этом примере входный текст представляет программу на языке, подобном языку Паскаль, и состоящей из Паскале-подобных предложений. Этот язык программирования позволяет только предложения вида: IF THEN ELSE, IF THEN, DO WHILE и присваивание (ASSIGNMENT). Предложения состоят из выражений и вложенных предложений. Выражения - сложение, вычитание, переменные и целые.&lt;br /&gt;
&lt;br /&gt;
Далее, как это работает:&lt;br /&gt;
&lt;br /&gt;
*Первый клауз парсера, s_program, принимает список токенов и пытается преобразовать его в список предложений.&lt;br /&gt;
*Предикат s_statement_list принимает тот же самый список токенов и проверяет возможность деления токенов на предложения, завершающиеся точкой с запятой.&lt;br /&gt;
*Предикат s_statement проверяет могут ли начальные токены списка (токенов) представлять собой правильное предложение. Если да, то такое предложение возвращается в виде структуры, а остаток токенов передается рекурсивно вызываемому предикату s_statement_list.&lt;br /&gt;
*Четыре клауза предиката s_statement соответствуют четырем типам, которые парсер понимает.&amp;lt;br\&amp;gt; Если первый клауз предиката s_statement не может преобразовать список токенов в предложение вида IF THEN ELSE, то клауз завершается неуспешно и в порядке отката переходит к следующему клаузу предиката s_statement. Теперь делается попытка преобразовать список токенов в конструкцию вида IF THEN. Если эта попытка неуспешна, то в следующем клаузе делается попытка преобразования к предложению вида DO WHILE.&lt;br /&gt;
*Если первые три клауза предиката s_statement завершаются неуспешно, то последний клауз этого предиката провереяет представлена ли в списке токенов операция присваивания. Этот клауз проверяет &amp;quot;на присваивание&amp;quot; является ли первый терм символом, второй - знаком равно (&amp;quot;=&amp;quot;), а следующие термы представляют простое математическое выражение.&lt;br /&gt;
*Предикаты s_exp, s_exp1 и s_exp2 работают аналогично, путем проверки являются ли начальные термы выражениями и, если это так, то предикату s_statement предикат s_exp возвращает остаток термов и математическое выражение в виде структуры.&lt;br /&gt;
&lt;br /&gt;
==Заключение==&lt;br /&gt;
&lt;br /&gt;
В этом руководстве раскрыта следующие важные моменты:&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;Списки&amp;#039;&amp;#039; могут содержать произвольное число элементов; Вы объявляете их простым добавление звездочки (&amp;#039;&amp;#039;&amp;#039;*&amp;#039;&amp;#039;&amp;#039;) в конце ранее определённого домена.&lt;br /&gt;
*Список является рекурсивным составным объектом, состоящим из головы и хвоста. Голова есть первый элемент, а хвост - остальная часть списка (без первого элемента). Хвост списка - всегда список; голова списка - всегда элемента. Список может содержать ноль или более элементов; Пустой список обозначается [].&lt;br /&gt;
*Элементами списка может быть что угодно, включая другие списки; все элементы списка должны принадлежать одному домену. В этом случае объявление домена элементов должно выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где elements = один из стандартных доменов (integer, real, etc.) или набор альтернатив  обозначенных различными функторами (int(integer); rl(real); smb(symbol); и т.д.). Смешение типов в списках языка системы Visual Prolog допускается только включением их в составные объекты или функторы.&lt;br /&gt;
*Можно использовать разделители (запятые, [ и |) для явного отделения головы списка от хвоста. Так, список&lt;br /&gt;
&amp;lt;vip&amp;gt;[a, b, c, d]&amp;lt;/vip&amp;gt;&lt;br /&gt;
может быть записан как:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
[a|[b, c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b|[c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b, c|[d]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c, d]]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c|[d]]]] &lt;br /&gt;
или даже&lt;br /&gt;
[a|[b|[c|[d|[]]]]]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Обработка списков заключается в рекурсивном отщеплении головы списка (и выполнении действий над ней) до опустошения списка.&lt;br /&gt;
*Предикаты для работы со списками содержаться в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
*Visual Prolog поддерживает встроенную конструкцию обработки списков, которая принимает целевой предикат в качестве одного из аргументов и собирает все решения этого целевого предиката в едином списке. Синтаксис такой конструкции&lt;br /&gt;
&amp;lt;vip&amp;gt;Result = [ Argument || myPredicate(Argument) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы одному и тому же домену, следует использовать функторы для создания списков, хранящих элементы различного типа.&lt;br /&gt;
*процесс &amp;#039;&amp;#039;разбора с использованием разностных списков (parsing by difference lists)&amp;#039;&amp;#039; работает путем сокращения задачи; пример в этом руководстве преобразует строку входных данных в структуру, которая может обрабатываться позже.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[[en:Lists and Recursion]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1592</id>
		<title>Списки и рекурсия</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1592"/>
		<updated>2007-11-23T11:01:40Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: очепятки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Обработка списков как обработка последовательности элементов - это мощная техника, используемая в Прологе. В этом руководстве мы рассматриваем что такое списки, как их объявлять, и затем приводим несколько примеров, показывающих как использовать работу со списками в приложениях. Мы также определяем два хорошо известных предиката Пролога – member (член, элемент) и append (добавить) – рассматривая обработку списков как с рекурсивной, так и процедурной точки зрения.&lt;br /&gt;
&lt;br /&gt;
Затем мы представляем предикат findall - стандартный предикат языка системы Visual Prolog, позволяющий собирать решения в единую цель. В завершение этого руководства мы обсуждаем составные списки – комбинции различных типов элементов и, кроме того, - пример разбора с помощью разностных списков.&lt;br /&gt;
&lt;br /&gt;
==Что такое Список?==&lt;br /&gt;
&lt;br /&gt;
В Прологе &amp;#039;&amp;#039;список (list)&amp;#039;&amp;#039; является объектом, содержащим внутри произвольное число других объектов. Списки соответствуют, грубо говоря, массивам в других языках, но, в отличие от массивов, список не трубует декларирования его размера до начала его использования.&lt;br /&gt;
&lt;br /&gt;
Список, содержащий числа 1, 2 и 3 записывается как &lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 3 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порядок элементов в этом списке значим:&lt;br /&gt;
*Число &amp;quot;1&amp;quot; является первым элементом,&lt;br /&gt;
*&amp;quot;2&amp;quot; - второй,&lt;br /&gt;
*&amp;quot;3&amp;quot; - третий.&lt;br /&gt;
&lt;br /&gt;
Список [ 1, 2, 3 ] и список [ 1, 3, 2 ] различны.&lt;br /&gt;
&lt;br /&gt;
Каждый компонент списка называется элемент (element). Для того, чтобы сформировать списковую структуру данных, следует разделять элементы запятыми и заключать их всех в квадратные скобки. Посмотрим на некоторые примеры:&lt;br /&gt;
&amp;lt;vip&amp;gt;[&amp;quot;dog&amp;quot;, &amp;quot;cat&amp;quot;, &amp;quot;canary&amp;quot;]&lt;br /&gt;
[&amp;quot;valerie ann&amp;quot;, &amp;quot;jennifer caitlin&amp;quot;, &amp;quot;benjamin thomas&amp;quot;]&amp;lt;/vip&amp;gt;&lt;br /&gt;
Один и тот же элемент может быть представлен в списке несколько раз, например:&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 1, 3, 1 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Объявление Списков===&lt;br /&gt;
Для объявления домена - списка целых используется декларация домена, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Звездочка означает &amp;quot;список этого&amp;quot;; то есть, integer* означает &amp;quot;список целых&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на то, что слово &amp;#039;&amp;#039;&amp;quot;list&amp;quot;&amp;#039;&amp;#039; не имеет специального значения в Visual Prolog. Вы равным образом могли бы назвать Ваш списковый домен как &amp;#039;&amp;#039;zanzibar&amp;#039;&amp;#039;. Именно звездочка, а не имя, предписывает этому домену быть списком.&lt;br /&gt;
&lt;br /&gt;
Элементами в списке может быть что угодно, включая другие списки. Но все элементы в списке должны принадлежать одному домену, и дополнительно к декларации спискового домена должна быть декларация &amp;#039;&amp;#039;&amp;#039;domains&amp;#039;&amp;#039;&amp;#039; для элементов:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;#039;&amp;#039;elements&amp;#039;&amp;#039; должны быть приравнены к простым доменным типам (например, integer, real или symbol) или к набору возможных альтернатив, обозначенных различными функторами. Visual Prolog не допускает смешивание стандартных типов в списке. Например, следующие декларации ошибочно представляют списки, созданные из integers, reals и symbols:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    integer;&lt;br /&gt;
    real;&lt;br /&gt;
    symbol.&lt;br /&gt;
        /* Неправильно */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходом для объявления списков из integer, real и symbols является объявление домена общего для всех типов, где функтор показывает какому типу принадлежит тот или иной элемент. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    i(integer);&lt;br /&gt;
    r(real);&lt;br /&gt;
    s(symbol).&lt;br /&gt;
        /* функторами являются i, r и s */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Подробнее об этом - в этом же руководстве в разделе &amp;quot;Составные списки&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
====Головы и Хвосты====&lt;br /&gt;
&lt;br /&gt;
Список на самом деле является рекурсивным составным объетом. Он состоит из двух частей - головы списка, которым является первый элемент, и хвоста - списка, который включает все следующие элементы.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Хвост списка всегда есть список; голова списка есть элемент.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Например,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;голова списка [a, b, c] есть a&lt;br /&gt;
хвост списка [a, b, c] есть [b, c]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Что происходит, когда мы имеем дело со списком, содержащим один элемент? Ответом является:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;головой списка [c] является c&lt;br /&gt;
хвостом списка [c] является []&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Если многократно отнимать первый элемент от хвоста списка, мы получим в конечном итоге пустой список (&amp;#039;&amp;#039;&amp;#039;[ ]&amp;#039;&amp;#039;&amp;#039;).&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Пустой список не может быть разбит на голову и хвост.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Это означает, что, концептуально говоря, списки имеют древовидную структуру подобно другим составным объектам. Древовидная структура списка [a, b, c, d] есть:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a    list&lt;br /&gt;
      /    \&lt;br /&gt;
     b    list&lt;br /&gt;
         /    \&lt;br /&gt;
        c    list&lt;br /&gt;
            /    \&lt;br /&gt;
           d      []&amp;lt;/pre&amp;gt;&lt;br /&gt;
Более того, одноэлементный список, такой как [a] - это не тот же самый элемент, который этот список содержит, поскольку [a] является действительно составной структурой данных, как это видно здесь:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a     []&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Представления Списков==&lt;br /&gt;
&lt;br /&gt;
Пролог содержит метод для явного обозначения головы и хвоста списка. Вместо разделения элементов запятыми можно отделять голову от хвоста вертикальной чертой (|). Например,&lt;br /&gt;
&lt;br /&gt;
[a, b, c] эквивалентно [a|[b, c]]&lt;br /&gt;
&lt;br /&gt;
и, продолжая процесс,&lt;br /&gt;
&lt;br /&gt;
[a|[b,c]] эквивалентно [a|[b|[c]]],&lt;br /&gt;
&lt;br /&gt;
что эквивалентно [a|[b|[c|[]]]]&lt;br /&gt;
&lt;br /&gt;
Можно даже использовать оба способа разделения в одном и том же списке, рассматривая вертикальную черту как разделитель самого низкого уровня. Следовательно, можно записать [a, b, c, d] как [a, b|[c, d]]. Таблица 1 дает дополнительные примеры.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 1: Головы и Хвосты списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список&lt;br /&gt;
!Голова&lt;br /&gt;
!Хвост&lt;br /&gt;
|-&lt;br /&gt;
|[&amp;#039;a&amp;#039;, &amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[&amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|-&lt;br /&gt;
|[ &amp;#039;a&amp;#039; ]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[] &lt;br /&gt;
|-&lt;br /&gt;
|/*пустой список*/ [ ]&lt;br /&gt;
|неопределен&lt;br /&gt;
|неопределен&lt;br /&gt;
|-&lt;br /&gt;
|[[1, 2, 3], [2, 3, 4], []]&lt;br /&gt;
|[1, 2, 3]&lt;br /&gt;
|[[2, 3, 4], []]&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
В Таблице 2 приведены некоторые примеры унификации списков.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 2: Унификация Списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список 1&lt;br /&gt;
!Список 2&lt;br /&gt;
!Связывание Переменных&lt;br /&gt;
|-&lt;br /&gt;
|[X, Y, Z]&lt;br /&gt;
|[эгберт, ест, мороженое]&lt;br /&gt;
|X=эгберт, Y=ест, Z=мороженое&lt;br /&gt;
|-&lt;br /&gt;
|[7]&lt;br /&gt;
|[X &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Y] &lt;br /&gt;
|X=7, Y=[]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2, 3, 4]&lt;br /&gt;
|[X, Y &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Z]&lt;br /&gt;
|X=1, Y=2, Z=[3,4]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2]&lt;br /&gt;
|[3 &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; X]&lt;br /&gt;
|fail&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Использование Списков==&lt;br /&gt;
&lt;br /&gt;
Поскольку списки являются в действительности рекурсивными составными структурами данных, для их обработки необходимы и рекурсивные алгоритмы. Самый естественный способ обработки списков - сквозной просмотр, в ходе которого что-то делается с каждым элементом, до тех пор, пока не достигнут конец.&lt;br /&gt;
&lt;br /&gt;
Как правило, такого рода алгоритмы используют два клауза. Один из них говорит о том, как поступать с обыкновенным списком, который может быть разделен на голову и хвост. Другой говорит о том, что делать с пустым списком.&lt;br /&gt;
&lt;br /&gt;
===Вывод Списков на печать===&lt;br /&gt;
&lt;br /&gt;
Например, если Вы хотите только вывести на печать элементы списка, то вот что Вы делаете:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  write_a_list : (integer*).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  write_a_list([]). /* Если список пустой, ничего не делаем. */&lt;br /&gt;
  write_a_list([H|T]):- /* Сопоставляем голову с H и хвост с T, и... */&lt;br /&gt;
    stdio::write(H),stdio::nl, /*выводим H и переводим строку*/&lt;br /&gt;
    write_a_list(T).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь мы видим два клауза write_a_list, которые можно выразить но обычном языке:&lt;br /&gt;
&lt;br /&gt;
*Для вывода на печать пустого списка ничего не надо делать.&lt;br /&gt;
*Иначе, для вывода на печать списка, вывести на печать его голову (она есть просто элемент), и потом вывести на печать хвост списка (он, как известно, есть список).&lt;br /&gt;
&lt;br /&gt;
Первый раз, когда вызывается:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такой вызов сопоставляется со вторым клаузом, с головой H=1 и T=[2, 3]. Это приводит к выводу на печать 1, затем рекурсивно вызывается write_a_list с аргументом в виде хвоста списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([2, 3]).&lt;br /&gt;
  /* Это вызов write_a_list(T). */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот второй вызов опять сопоставляется со вторым клаузом, где, на этот раз H=2 и T=[3], поэтому выводится 2 и опять рекурсивно вызывается write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С каким клаузом теперь такой вызов сопоставлятся? Напомним, что, хотя список [3] имеет всего один элемент, у него есть голова и хвост - голова есть 3, а хвост есть []. Таким образом, этот вызов опять сопоставляется со вторым клаузом с H=3 и T=[]. Теперь выводится 3 и  вызывается рекурсивно write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь становится понятно для чего нужен первый клауз. Второй клауз не может быть сопоставлен с таким вызовом, поскольку [] не может быть разделен на голову и хвост. Если бы первого клазуа здесь не было бы, то выполнение goal оказалось бы неуспешным. Но, поскольку он есть, то первый клауз сопоставляется с вызовом и выполнение goal успешно завершается и нечего более не делается.&lt;br /&gt;
&lt;br /&gt;
===Подсчет элементов в Списке===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь, как подсчитать число элементов в списке, или какова длина списка? Логично определить:&lt;br /&gt;
&lt;br /&gt;
*Длина пустого списка [] есть 0.&amp;lt;br /&amp;gt;&lt;br /&gt;
*Длина любого другого списка есть 1 плюс длина его хвоста.&lt;br /&gt;
&lt;br /&gt;
Можно ли это запрограммировать? На Прологе это очень просто. Всего два клауза:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], 0).&lt;br /&gt;
  length_of([_|T], L):-&lt;br /&gt;
    length_of(T, TailLength),&lt;br /&gt;
    L = TailLength + 1.&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L),&lt;br /&gt;
  stdio::write(L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотрите прежде всего на второй клауз. Строго говоря, [_|T] сопоставляется с любым непустым списком, связывая T с хвостом списка. Значение головы неважно, если она есть, она может быть учтена как один элемент.&lt;br /&gt;
&lt;br /&gt;
Тогда вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется со вторым клаузом, с T=[2, 3]. Следующим шагом является вычисление длины хвоста T. Когда это сделано (не имеет значение, как), TailLength получит значение 2, и компьютер теперь может добавить 1 к ней и связать L со значением 3. Как выполняется этот промежуточный шаг? Надо найти длину списка [2, 3], путем удовлетворения цели&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([2, 3], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Другими словами, length_of вызывает себя рекурсивно. Этот вызов сопоставляется со вторым клаузом, связывая&lt;br /&gt;
&lt;br /&gt;
*[3] и T в вызове клаузы и&lt;br /&gt;
*TailLength с L в клаузе.&lt;br /&gt;
&lt;br /&gt;
Подчеркиваем, TailLength в вызове никак не пересекается с TailLength в клаузе, поскольку &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;каждый рекурсивный вызов клауза имеет собственный набор переменных&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Итак, теперь задача - найти длину списка [3], которая есть 1, и мы добавляем 1 к этому значению, чтобы получить длину списка [2, 3], что будет 2. Ну и хорошо!.&lt;br /&gt;
&lt;br /&gt;
Аналогично, length_of вызывает себя рекурсивно опять для получения длины списка [3]. Хвост  [3] есть [], поэтому T связвается с [], и задача теперь - получение длины списка [] и добавление к ней 1, что дает длину списка [3].&lt;br /&gt;
&lt;br /&gt;
Теперь все просто. Цель:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется с &amp;#039;&amp;#039;первым&amp;#039;&amp;#039; клаузом, связывая TailLength с 0. Поэтому теперь компьютер может добавить 1 к нему, получая длину списка [3], и возвращаясь теперь в вызывавший клауз. Это, в свою очередь, опять добавляет 1, давая длину списка [2, 3], и возвращается в клауз, который его вызывал; этот первоначальный клауз добавит снова 1, давая длину списка [1, 2, 3].&lt;br /&gt;
&lt;br /&gt;
Не растерялись? Мы надеемся, нет. В следующей короткой иллюстрации мы сводим воедино все вызовы. Мы использовали здесь прием подстрочника для того, чтобы показать, что аналогично называемые переменные в разных клаузах или различные вызовы того же самого клауза - одно и то же. &lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L1).&lt;br /&gt;
my::length_of([2, 3], L2).&lt;br /&gt;
my::length_of([3], L3).&lt;br /&gt;
my::length_of([], 0).&lt;br /&gt;
L3 =  0+1 = 1.&lt;br /&gt;
L2 = L3+1 = 2.&lt;br /&gt;
L1 = L2+1 = 3.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что Вам не нужно каждый раз создавать такого рода предикаты самостоятельно, Вы можете использовать готовый предикат &amp;#039;&amp;#039;&amp;#039;list::length&amp;#039;&amp;#039;&amp;#039; из PFC.&lt;br /&gt;
&lt;br /&gt;
===Хвостовая рекурсия===&lt;br /&gt;
&lt;br /&gt;
Вы, очевидно, заметили, что length_of не является (и не может быть) предикатом с хвостовой рекурсией, поскольку рекурсивный вызов не является последним шагом в его клаузе. Возможно ли создать предикат, определяющий длину, так, чтобы он был предикатом с хвостовой рекурсией? Да, но это потребует некоторых усилий.&lt;br /&gt;
&lt;br /&gt;
Проблема с предикатом length_of в том, что длину списка нельзя вычислить до тех пор, пока не вычислена длина его хвоста. Но из этой ситуации есть выход. Нам потребуется предикат, вычисляющий длину списка, с тремя аргументами.&lt;br /&gt;
&lt;br /&gt;
*Один из них - это список, от которого компьютер будет откусывать по одному элементу на каждом вызове до тех пор, пока этот список, как и прежде, не превратится в пустой список.&lt;br /&gt;
*Второй - это свободный аргумент, который в конечном итоге вернет результат (длину).&lt;br /&gt;
*Третий - это счетчик, значение которого начинается с нуля и увеличивается с каждым вызовом.&lt;br /&gt;
&lt;br /&gt;
Когда список в конечном итоге станет пустым, мы проунифицируем счетчик с несвязанным результатом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer, integer) procedure(i,o,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], Result, Result).&lt;br /&gt;
  length_of([_|T], Result, Counter):-&lt;br /&gt;
    NewCounter = Counter + 1,&lt;br /&gt;
    length_of(T, Result, NewCounter).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L, 0), /* Начинаем со счетчиком Counter = 0 */&lt;br /&gt;
  stdio::write(&amp;quot; L = &amp;quot;, L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта версия предиката length_of более сложная и во многих смыслах менее логичная, чем предыдущая. Мы ее представили здесь главным образом для того, чтобы показать, что на практике &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;вы можете часто построить алгоритм с хвостовой рекурсией для задач, которые на первый взгляд требуют рекурсии другого типа&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
===Модификация Списка===&lt;br /&gt;
&lt;br /&gt;
Иногда требуется создать другой список из заданного списка. Это делается путем просмотра списка, элемент за элементом, заменяя каждый элемент вычисленным значением. Например, как эта программа, которая добавляет 1 к каждому элементу исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  add1 : (integer*, integer*) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  add1([], [])./* граничное условие */&lt;br /&gt;
  add1([Head|Tail],[Head1|Tail1]):- /* отделяем голову от остального списка*/&lt;br /&gt;
    Head1 = Head+1, /* добавляем 1 к элементу-голове */&lt;br /&gt;
    add1(Tail, Tail1)./* далаем это с остальной частью списка*/&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::add1([1,2,3,4], NewList),&lt;br /&gt;
  stdio::write(NewList)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На обычном языке это звучит так:&lt;br /&gt;
*Добавление 1 ко всем элементам пустого списка порождаем пустой список,&lt;br /&gt;
*Для добавления 1 ко всем элемента любого другого списка:&lt;br /&gt;
**добавить 1 к голове и сделать эту голову головой результирующего списка, а затем&lt;br /&gt;
**добавить 1 к каждому элемента хвоста и этот хвост сделать хвостом результата.&lt;br /&gt;
&lt;br /&gt;
Загрузим программу и выполним такую цель&lt;br /&gt;
&amp;lt;vip&amp;gt;add1([1,2,3,4], NewList).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель вернет&lt;br /&gt;
&amp;lt;vip&amp;gt;NewList=[2,3,4,5]&lt;br /&gt;
1 Solution&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Опять о хвостовой рекурсии===&lt;br /&gt;
&lt;br /&gt;
Является ли предикат add1 проедикатом с хвостовой рекурсией? &lt;br /&gt;
Если у Вас есть опыт использования Lisp или Pascal, Вы могли бы подумать, что нет, поскольку Вы бы рассуждали так:&lt;br /&gt;
&lt;br /&gt;
*Делим список на &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;.&lt;br /&gt;
*Добавляем 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавляя 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
*Соединяем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, что дает результирующий список.&lt;br /&gt;
&lt;br /&gt;
Это не похоже на хвостовую рекурсию, поскольку последний шаг - не рекурсивный вызов.&lt;br /&gt;
&lt;br /&gt;
Однако, и это важно, – &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Это не то, что делает Пролог&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;. В Visual Prolog add1 является предикатом с хвостовой рекурсией, поскольку выполняется в действительности следующим образом:&lt;br /&gt;
&lt;br /&gt;
*Связать голову и хвост исходного списка с &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, соответственно.&lt;br /&gt;
*Связать голову и хвост результирующего списка с &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, соответственно. (&amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; пока не получили значений.)&lt;br /&gt;
*Добавить 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавить 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Когда это сделано, &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;уже являются&amp;#039;&amp;#039;&amp;#039; головой и списком результата и отдельной операции по их соединению нет. Поэтому рекурсивный вызов и является последним шагом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h6&amp;gt;Снова Модификация Списков&amp;lt;/h6&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Конечно, не всегда модификации подлежит каждый элемент. Посмотрим на программу, которая сканирует список чисел и копирует его, удаляя отрицательные числа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  discard_negatives : (integer*, integer*) procedure(i,o). /*удалить отрицательные*/&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  discard_negatives([], []).&lt;br /&gt;
  discard_negatives([H|T], ProcessedTail):-&lt;br /&gt;
    H &amp;lt; 0,&lt;br /&gt;
    !, /* Если H отрицательно, пропускаем его */&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
  discard_negatives([H|T], [H|ProcessedTail]):-&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::discard_negatives ([2, -45, 3, 468], X),&lt;br /&gt;
  stdio::write(X).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напрмер, цель&lt;br /&gt;
&amp;lt;vip&amp;gt;my::discard_negatives([2, -45, 3, 468], X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
дает&lt;br /&gt;
&amp;lt;vip&amp;gt;X=[2, 3, 468].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
А вот - предикат который копирует элементы списка, добавляя для каждого элемента его дубликат:&lt;br /&gt;
&amp;lt;vip&amp;gt;doubletalk([], []).&lt;br /&gt;
doubletalk([H|T], [H, H|DoubledTail]) :-&lt;br /&gt;
  doubletalk(T, DoubledTail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Принадлежнось списку===&lt;br /&gt;
&lt;br /&gt;
Допустим, имеется список с именами &amp;#039;&amp;#039;John&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Leonard&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Eric&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Frank&amp;#039;&amp;#039; и требуется, используя Visual Prolog, выяснить, принадлежит ли заданное имя этому списку. Другими словами, надо определить &amp;quot;отношение&amp;quot; между двумя аргументами: именем и списком имен. Это соответствует предикату&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember : (name, name*).&lt;br /&gt;
  /* &amp;quot;name&amp;quot; принадлежит списку &amp;quot;name*&amp;quot; */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В программе e01.pro первый клауз исследует голову списка. Если голова списка совпадает с искомым именем, то можно сделать заключение, что Name принадлежит списку. Поскольку хвост списка нас не интересует, то это мы представляем анонимной переменной. Благодаря первому клаузу, цель&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;])&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
удовлетворена.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Программа e01.pro */&lt;br /&gt;
class my&lt;br /&gt;
predicates&lt;br /&gt;
  isMember : (A, A*) determ.&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  isMember(Name, [Name|_]) :-&lt;br /&gt;
    !.&lt;br /&gt;
  isMember(Name, [_|Tail]):-&lt;br /&gt;
    isMember(Name,Tail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;]),&lt;br /&gt;
  !,&lt;br /&gt;
  stdio::write(&amp;quot;Success&amp;quot;)&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если голова списка не есть Name, то надо исследовать, не содержится ли Name в хвосте списка.&lt;br /&gt;
&lt;br /&gt;
На обычном языке:&lt;br /&gt;
&lt;br /&gt;
Name принадлежит списку, если Name является первым элементом списка, или&amp;lt;br /&amp;gt;&lt;br /&gt;
Name принадлежит списку, если Name принадлежит хвосту.&lt;br /&gt;
&lt;br /&gt;
Второй клауз предиката isMember относится к этому отношению. Таким образом на Visual Prolog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember(Name, [_|Tail]) :-&lt;br /&gt;
    isMember(Name, Tail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Добавление списка к другому списку: декларативное и рекурсивное решения===&lt;br /&gt;
&lt;br /&gt;
Рассмотренный предикат member программы e01.pro работает в двух направлениях. Вернемся к его клаузам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
member(Name, [Name|_]).&lt;br /&gt;
member(Name, [_|Tail]) :-&lt;br /&gt;
  member(Name, Tail).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На эти клаузы можно смотреть с двух различных точек зрения: декларативной и процедурной.&lt;br /&gt;
&lt;br /&gt;
*С декларативной точки зрения, клаузы выражают: &lt;br /&gt;
**Name (Имя) принадлежит списку, если голова списка есть Name&lt;br /&gt;
**иначе Name принадлежит списку, если оно (Имя) принадлежит хвосту.&lt;br /&gt;
*С процедурной точки зрения, эти же два клауза могут быть интерпретированы так:&amp;lt;br/&amp;gt;Чтобы найти элемент списка &lt;br /&gt;
**Найдите его голову&lt;br /&gt;
**иначе найдите элемент хвоста списка.&lt;br /&gt;
&lt;br /&gt;
Эти две точки зрения соответствуют целям&lt;br /&gt;
&amp;lt;vip&amp;gt;member(2, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;member(X, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате первый вызов поручает Visual Prolog(у) проверить, истино ли нечто (принадлежность числа 2 списку [1,2,3,4]). Второй вызов поручает Visual Prolog(у) найти все члены списка [1,2,3,4]. Не смущайтесь этим. Предикат member является одним и тем же, но на его поведение можно смотреть под разными углами.&lt;br /&gt;
&lt;br /&gt;
====Рекурсия с процедуральной точки зрения====&lt;br /&gt;
&lt;br /&gt;
Прелесть Пролога заключается в том, что часто, когда мы конструируем клаузы для предиката, будучи на одной точке зрения, они будут работать и при взгляде с другой точки зрения. Чтобы обнаружить эту дуальность, мы приведем пример предиката для добавления (append) одного списка к другому. Мы определяем предикат append с тремя аргументами:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append(List1, List2, List3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот предикат интегрирует списки List1 и List2 в форму списка List3 так, что список List2 дописывается в конце списка List1. То есть содержательно - осуществляется добавление списка List2 к списку LIst1. Опять мы используем рекурсию (на этот раз с процедуральной точки зрения).&lt;br /&gt;
&lt;br /&gt;
Если список List1 пустой, результатом добавления списка List1 к списку List2 будет тот же самый List2. Запишем это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([], List2, List2).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если список List1 не пустой, то можно преобразовать списки List1 и List2 к форме списка &amp;#039;&amp;#039;List3&amp;#039;&amp;#039;, сделав голову списка List1 головой списка List3. В приведенном коде переменная H используется в качестве головы как списка List1, так и списка List3. хвост списка List3 есть список L3, который составлен из остатка списка List1 (а именно, L1) и всего списка List2. Опять выразим это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([H|L1], List2, [H|L3]) :-&lt;br /&gt;
   append(L1, List2, L3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append работает следующим образом: пока список List1 не пустой, рекурсивное правило дописывает один элемент каждый раз к списку List3. Когда список List1 становится пустым, первый клауз  clause обеспечивает дописывание списка List2 в конец списка List3.&lt;br /&gt;
&lt;br /&gt;
====Варианты использования одного предиката====&lt;br /&gt;
&lt;br /&gt;
Подходя к предикату append с декларативной точки зрения, мы определили его как отношение между тремя списками. Это отношение справедливо также, если списки List1 и List3 известны, а List2 - нет. Более того, это также работает, если только List3 известен. Например, для того, чтобы выяснить, какие два списка могли бы быть соединены для получения известного списка, можно использовать вызов в форме&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, L2, [1, 2, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С таким целевым вызовом, Visual Prolog найдет следующие решения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
L1=[], L2=[1,2,4]&lt;br /&gt;
L1=[1], L2=[2,4]&lt;br /&gt;
L1=[1,2], L2=[4]&lt;br /&gt;
L1=[1,2,4], L2=[]&lt;br /&gt;
4 Solutions&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно использовать предикат append для нахождения списка, который следовало бы добавить к списку [3,4] для получения списка[1,2,3,4]. Попробуем такой вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, [3,4], [1,2,3,4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Visual Prolog находит решение&lt;br /&gt;
&amp;lt;vip&amp;gt;L1=[1,2].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append определяет отношение между &amp;#039;&amp;#039;входным набором (input set)&amp;#039;&amp;#039; и &amp;#039;&amp;#039;выходным набором (output set)&amp;#039;&amp;#039; таким образом, что отношение применимо в обе стороны. При таком отношении возникает вопрос&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Что является выходным набором для заданного входного?&amp;#039;&amp;#039; или &amp;#039;&amp;#039;Какой входной набор соответствует заданному выходному?&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Статус аргументов данного вызова предиката известен как &amp;#039;&amp;#039;поток (или шаблон) ввода-вывода&amp;#039;&amp;#039;. Аргумент, который связан или наследуется в момент вызова является входным аргументом и обозначается как (i). Свободный аргумент является выходным аргументом и обозначается как (o).&lt;br /&gt;
&lt;br /&gt;
Предикат append обладает свойством поддерживать любой шаблон ввода-вывода, какой требуется. Однако не все предикаты имеют возможность вызова с различными шаблонами ввода-вывода. Когда клауз Пролога способен поддерживать множество шаблонов ввода-вывода, он называется инверсным клаузом. Целый набор предикатов для обработки списков содержится в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Все Решения Сразу==&lt;br /&gt;
&lt;br /&gt;
Откаты и рекурсии являются двумя способами выполнения повторяющихся процессов. Рекурсия предпочтительнее, поскольку, в отличие от отката, позволяет передать данные через аргументы от одного рекурсивного вызова к следующему. Благодаря этому, рекурсивные процедуры могут использовать промежуточные результаты или счетчики по ходу выполнения.&lt;br /&gt;
&lt;br /&gt;
Однако есть одна вещь, которую, в отличие от рекурсии, могут делать откаты, а именно – искать все альтернативные решения посредством одного вызова. Поэтому Вы можете оказаться в затруднительном положении: Вам нужно получить все решения за один вызов, и, при этом, они Вам нужны все сразу, как часть единой интегрированной структуры данных. Что делать?&lt;br /&gt;
&lt;br /&gt;
К счастью, Visual Prolog позволяет найти выход из этого положения. Предопределенная конструкция обработки списков получает целевой вызов в качестве своего аргумента и собирает все решения для этого вызова в единый выходной список. Такая конструкция для списков имеет два аргумента:&lt;br /&gt;
&lt;br /&gt;
*Первый аргумент, VarName, определяет аргумент в вызываемом целевом предикате, значения которого будут собираться в список.&lt;br /&gt;
*Второй - mypredicate - определяет предикат, который будет получать значения.&lt;br /&gt;
*Выходной параметр ListParam, является переменной, которая содержит список значений, полученных в ходе отката.&lt;br /&gt;
&lt;br /&gt;
Программа e02.pro использует обработку списка для вывода среднего возраста группы людей.&lt;br /&gt;
&amp;lt;vip&amp;gt;        /* Программа e02.pro */&lt;br /&gt;
class my&lt;br /&gt;
domains&lt;br /&gt;
  name = string.&lt;br /&gt;
  address = string.&lt;br /&gt;
  age = integer.&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  person : (name, address, age) nondeterm anyflow.&lt;br /&gt;
  sumlist : (age*, age, integer)procedure(i,o,o).&lt;br /&gt;
end class my&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  sumlist([],0,0).&lt;br /&gt;
  sumlist([H|T], Sum, N):-&lt;br /&gt;
    sumlist(T, S1, N1),&lt;br /&gt;
    Sum=H+S1, N=1+N1.&lt;br /&gt;
  &lt;br /&gt;
  person(&amp;quot;Sherlock Holmes&amp;quot;,&amp;quot;22B Baker Street&amp;quot;, 42).&lt;br /&gt;
  person(&amp;quot;Pete Spiers&amp;quot;,&amp;quot;Apt. 22, 21st Street&amp;quot;, 36).&lt;br /&gt;
  person(&amp;quot;Mary Darrow&amp;quot;,&amp;quot;Suite 2, Omega Home&amp;quot;, 51).&lt;br /&gt;
end implement my&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  L = [ Age || my::person(_, _, Age)],&lt;br /&gt;
  my::sumlist(L, Sum, N),&lt;br /&gt;
  Ave = Sum/N,&lt;br /&gt;
  stdio::write(&amp;quot;Average=&amp;quot;, Ave, &amp;quot;. &amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Клауз обработки списка в этой программе создает список L, который является списком всех возрастов, полученных от предиката person. Если бы потребовалось бы собрать список всех людей в возрасте 42 лет, можно было бы задать следующий цель-вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ Who || my::person(Who, _, 42) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий код порождает список всех положительных чисел исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ X || X = list::getMember_nd([2,-8,-3,6]), X &amp;gt; 0]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Смешанные списки==&lt;br /&gt;
&lt;br /&gt;
Список целых объявляется просто&lt;br /&gt;
&amp;lt;vip&amp;gt;integer*&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же верно и для списков вещественных чисел (integer), символьных (symbol) списков или списков строк (string).&lt;br /&gt;
&lt;br /&gt;
Однако часто приходится хранить комбинацию различных типов элементов в составе одного и того же списка, такую как:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[2, 3, 5.12, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot;].&lt;br /&gt;
        /* Не корректно для Visual Prolog*/&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Смешанные списки&amp;#039;&amp;#039; - списки, содержащие элементы более чем одного типа. Для работы со списками разнотипных элементов должны использоваться специальные объявления, поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;одному и тому же&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; домену. Способом создания списка, который хранит такие различные типы элементов является использование функторов, поскольку домен может представляться &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;более, чем одним&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; функтором, каждый со своими т&amp;#039;&amp;#039;&amp;#039;и&amp;#039;&amp;#039;&amp;#039;повыми аргументами.&lt;br /&gt;
&lt;br /&gt;
Пример объаявления списка, который может хранить целые, символы, строки или списки таких  даных:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  /* функторами являются l, i, c, and s */&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Список&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 2, 9, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot; ]&lt;br /&gt;
/* Не корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
на Visual Prolog следовало бы записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[i(2), i(9), l([s(&amp;quot;food&amp;quot;), s(&amp;quot;goo&amp;quot;)]), s(&amp;quot;new&amp;quot;)]&lt;br /&gt;
        /* Корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример предиката append показывает как использовать такого рода декларации в стандартных программах манипулирования списками.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  append : (A*,A*,A*) procedure (i,i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  append([], L, L).&lt;br /&gt;
  append([X|L1], L2, [X|L3]):-&lt;br /&gt;
    append(L1, L2, L3).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::s(&amp;quot;likes&amp;quot;),my::l([my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;mary&amp;quot;)])],&lt;br /&gt;
    [my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;sue&amp;quot;)], &lt;br /&gt;
    Ans&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Первый список: &amp;quot;, Ans,&amp;quot;\n\n&amp;quot;),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::l([my::s(&amp;quot;This&amp;quot;),my::s(&amp;quot;is&amp;quot;),my::s(&amp;quot;a&amp;quot;),my::s(&amp;quot;list&amp;quot;)]),my::s(&amp;quot;bee&amp;quot;)], &lt;br /&gt;
    [my::c(&amp;#039;c&amp;#039;)], &lt;br /&gt;
    Ans2&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Второй списко: &amp;quot;, Ans2, &amp;quot;\n\n&amp;amp;quot).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Разбор с использованием списков==&lt;br /&gt;
&lt;br /&gt;
Ниже приведена программа, демонстрирующая [[wikipedia:parsing|разбор]] (parsing)с использованием списков. Процесс разбора работает в методом сворачивания. В этом примере входная строка преобразуется в структуру данных Пролога, которая может быть использована далее.&lt;br /&gt;
&lt;br /&gt;
Этот разборщик предназначен для примитивного формального языка. Хотя этот пример достаточно сложен с точки зрения этого руководства, мы решили поместит его здесь, поскольку разбор является одной из областей, где Visual Prolog очень эффективен. Если Вы чувствуете себя не вполне готовым для этого раздела, Вы можете этот пример пропустить и продолжить чтение руководства без потери содержательности.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;#include @&amp;quot;pfc\exception\exception.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\string\string.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\console\console.ph&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class my_t&lt;br /&gt;
predicates&lt;br /&gt;
  tokl : (string, string*) procedure (i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_t&lt;br /&gt;
clauses&lt;br /&gt;
  tokl(Str, [H|T]) :-&lt;br /&gt;
    string::fronttoken(Str, H, Str1),&lt;br /&gt;
    !,&lt;br /&gt;
    tokl(Str1, T).&lt;br /&gt;
  tokl(_, []).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
/* * * * * * * * * * * * * * * * * * * *  *&lt;br /&gt;
* Вторая часть этой программы - разборщик *&lt;br /&gt;
* * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
class my_p&lt;br /&gt;
domains&lt;br /&gt;
  program = program(statement*).&lt;br /&gt;
  /* * * * * * * * * * * * * * * * * * * * *  * &lt;br /&gt;
  * Определение что есть языковая конструкция *&lt;br /&gt;
  * (предложение языка)                       *&lt;br /&gt;
  * * * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
  statement =&lt;br /&gt;
    if_Then_Else(exp, statement, statement);&lt;br /&gt;
    if_Then(exp, statement);&lt;br /&gt;
    while(exp, statement);&lt;br /&gt;
    assign(id, exp).&lt;br /&gt;
  /* * * * * * * * * * *  *&lt;br /&gt;
  * Определение выражения *&lt;br /&gt;
  * * * * * * * *  * * *  */&lt;br /&gt;
  exp = &lt;br /&gt;
    plus(exp, exp);&lt;br /&gt;
    minus(exp, exp);&lt;br /&gt;
    var(id);&lt;br /&gt;
    int(integer).&lt;br /&gt;
    id = string.&lt;br /&gt;
&lt;br /&gt;
  predicates&lt;br /&gt;
    s_program : (string*, program) procedure (i,o).&lt;br /&gt;
    s_statement : (string*, string*, statement) determ (i,o,o).&lt;br /&gt;
    s_statement_list : (string*, string*, statement*) determ (i,o,o).&lt;br /&gt;
    s_exp : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
    s_exp1 : (string*, string*, exp, exp) determ (i,o,i,o).&lt;br /&gt;
    s_exp2 : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_p&lt;br /&gt;
clauses&lt;br /&gt;
  s_program(TokenList, program(StatementList)):-&lt;br /&gt;
    s_statement_list(TokenList, _, StatementList),&lt;br /&gt;
    !.&lt;br /&gt;
  s_program(_, program([])).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement_list([], [], []) :- !.&lt;br /&gt;
  s_statement_list(List1, List4, [Statement|Program]) :-&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;;&amp;quot;|List3],&lt;br /&gt;
    s_statement_list(List3, List4, Program).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List7,if_then_else(Exp,Statement1, Statement2)):-&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement1),&lt;br /&gt;
    List4=[&amp;quot;else&amp;quot;|List5],!,&lt;br /&gt;
    s_statement(List5, List6, Statement2),&lt;br /&gt;
    List6=[&amp;quot;fi&amp;quot;|List7].&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List5,if_then(Exp, Statement)) :- !,&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement),&lt;br /&gt;
    List4=[&amp;quot;fi&amp;quot;|List5].&lt;br /&gt;
  s_statement([&amp;quot;do&amp;quot;|List1], List4,while(Exp, Statement)) :- !,&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;while&amp;quot;|List3],&lt;br /&gt;
    s_exp(List3, List4, Exp).&lt;br /&gt;
  s_statement([ID|List1], List3,assign(Id,Exp)) :-&lt;br /&gt;
    string::isname(ID),&lt;br /&gt;
    List1=[&amp;quot;=&amp;quot;|List2],&lt;br /&gt;
    s_exp(List2, List3, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp(List1, List3, Exp):-&lt;br /&gt;
    s_exp2(List1, List2, Exp1),&lt;br /&gt;
    s_exp1(List2, List3, Exp1, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp1([&amp;quot;+&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, plus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1([&amp;quot;-&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, minus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1(List, List, Exp, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp2([Int|Rest], Rest, int(I)) :-&lt;br /&gt;
    trap(I = toTerm(Int),Error,exception::clear_fail(Error)),&lt;br /&gt;
    !.&lt;br /&gt;
  s_exp2([Id|Rest], Rest, var(Id)) :-&lt;br /&gt;
    string::isname(Id).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  stdio::write(Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res),&lt;br /&gt;
  stdio::write(Res).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Загрузите программу и выполните целевой вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;goal&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Prolog вернет структуру программы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;Ans = [&amp;quot;b&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;if&amp;quot;,&amp;quot;b&amp;quot;,&amp;quot;then&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;1&amp;quot;,&lt;br /&gt;
    &amp;quot;else&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;fi&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;do&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;a&amp;quot;,&lt;br /&gt;
    &amp;quot;-&amp;quot;,&amp;quot;1&amp;quot;,&amp;quot;while&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;;&amp;quot;],&lt;br /&gt;
Res=program&lt;br /&gt;
  (&lt;br /&gt;
  [assign(&amp;quot;b&amp;quot;,int(2)),&lt;br /&gt;
   if_then_else&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;b&amp;quot;),&lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(1)), &lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(2))&lt;br /&gt;
     ),&lt;br /&gt;
   while&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;a&amp;quot;),&lt;br /&gt;
     assign&lt;br /&gt;
       (&lt;br /&gt;
       &amp;quot;a&amp;quot;,&lt;br /&gt;
       minus&lt;br /&gt;
         (&lt;br /&gt;
         var(&amp;quot;a&amp;quot;),&lt;br /&gt;
         int(1)&lt;br /&gt;
         )&lt;br /&gt;
       )&lt;br /&gt;
     )&lt;br /&gt;
  ])&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Преобразование в этом примере представлено двумя шагами: сканирование и разбор. Предикат tokl  - это сканер. Он принимает строку и преобразует ее в список токенов. Все предикаты с именами, начинающимися с s_ являются предикатами парсера. В этом примере входный текст представляет программу на языке, подобном языку Паскаль, и состоящей из Паскале-подобных предложений. Этот язык программирования позволяет только предложения вида: IF THEN ELSE, IF THEN, DO WHILE и присваивание (ASSIGNMENT). Предложения состоят из выражений и вложенных предложений. Выражения - сложение, вычитание, переменные и целые.&lt;br /&gt;
&lt;br /&gt;
Далее, как это работает:&lt;br /&gt;
&lt;br /&gt;
*Первый клауз парсера, s_program, принимает список токенов и пытается преобразовать его в список предложений.&lt;br /&gt;
*Предикат s_statement_list принимает тот же самый список токенов и проверяет возможность деления токенов на предложения, завершающиеся точкой с запятой.&lt;br /&gt;
*Предикат s_statement проверяет могут ли начальные токены списка (токенов) представлять собой правильное предложение. Если да, то такое предложение возвращается в виде структуры, а остаток токенов передается рекурсивно вызываемому предикату s_statement_list.&lt;br /&gt;
*Четыре клауза предиката s_statement соответствуют четырем типам, которые парсер понимает.&amp;lt;br\&amp;gt; Если первый клауз предиката s_statement не может преобразовать список токенов в предложение вида IF THEN ELSE, то клауз завершается неуспешно и в порядке отката переходит к следующему клаузу предиката s_statement. Теперь делается попытка преобразовать список токенов в конструкцию вида IF THEN. Если эта попытка неуспешна, то в следующем клаузе делается попытка преобразования к предложению вида DO WHILE.&lt;br /&gt;
*Если первые три клауза предиката s_statement завершаются неуспешно, то последний клауз этого предиката провереяет представлена ли в списке токенов операция присваивания. Этот клауз проверяет &amp;quot;на присваивание&amp;quot; является ли первый терм символом, второй - знаком равно (&amp;quot;=&amp;quot;), а следующие термы представляют простое математическое выражение.&lt;br /&gt;
*Предикаты s_exp, s_exp1 и s_exp2 работают аналогично, путем проверки являются ли начальные термы выражениями и, если это так, то предикату s_statement предикат s_exp возвращает остаток термов и математическое выражение в виде структуры.&lt;br /&gt;
&lt;br /&gt;
==Заключение==&lt;br /&gt;
&lt;br /&gt;
В этом руководстве раскрыта следующие важные моменты:&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;Списки&amp;#039;&amp;#039; могут содержать произвольное число элементов; Вы объявляете их простым добавление звездочки (&amp;#039;&amp;#039;&amp;#039;*&amp;#039;&amp;#039;&amp;#039;) в конце ранее определённого домена.&lt;br /&gt;
*Список является рекурсивным составным объектом, состоящим из головы и хвоста. Голова есть первый элемент, а хвост - остальная часть списка (без первого элемента). Хвост списка - всегда список; голова списка - всегда элемента. Список может содержать ноль или более элементов; Пустой список обозначается [].&lt;br /&gt;
*Элементами списка может быть что угодно, включая другие списки; все элементы списка должны принадлежать одному домену. В этом случае объявление домена элементов должно выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где elements = один из стандартных доменов (integer, real, etc.) или набор альтернатив  обозначенных различными функторами (int(integer); rl(real); smb(symbol); и т.д.). Смешение типов в списках языка системы Visual Prolog допускается только включением их в составные объекты или функторы.&lt;br /&gt;
*Можно использовать разделители (запятые, [ и |) для явного отделения головы списка от хвоста. Так, список&lt;br /&gt;
&amp;lt;vip&amp;gt;[a, b, c, d]&amp;lt;/vip&amp;gt;&lt;br /&gt;
может быть записан как:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
[a|[b, c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b|[c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b, c|[d]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c, d]]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c|[d]]]] &lt;br /&gt;
или даже&lt;br /&gt;
[a|[b|[c|[d|[]]]]]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Обработка списков заключается в рекурсивном отщеплении головы списка (и выполнении действий над ней) до опустошения списка.&lt;br /&gt;
*Предикаты для работы со списками содержаться в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
*Visual Prolog поддерживает встроенную конструкцию обработки списков, которая принимает целевой предикат в качестве одного из аргументов и собирает все решения этого целевого предиката в едином списке. Синтаксис такой конструкции&lt;br /&gt;
&amp;lt;vip&amp;gt;Result = [ Argument || myPredicate(Argument) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы одному и тому же домену, следует использовать функторы для создания списков, хранящих элементы различного типа.&lt;br /&gt;
*процесс &amp;#039;&amp;#039;разбора с использованием разностных списков (parsing by difference lists)&amp;#039;&amp;#039; работает путем сокращения задачи; пример в этом руководстве преобразует строку входных данных в структуру, которая может обрабатываться позже.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[[en:Lists and Recursion]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1591</id>
		<title>Списки и рекурсия</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1591"/>
		<updated>2007-11-23T10:59:53Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: очепятки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Обработка списков как обработка последовательности элементов - это мощная техника, используемая в Прологе. В этом руководстве мы рассматриваем что такое списки, как их объявлять, и затем приводим несколько примеров, показывающих как использовать работу со списками в приложениях. Мы также определяем два хорошо известных предиката Пролога – member (член, элемент) и append (добавить) – рассматривая обработку списков как с рекурсивной, так и процедурной точки зрения.&lt;br /&gt;
&lt;br /&gt;
Затем мы представляем предикат findall - стандартный предикат языка системы Visual Prolog, позволяющий собирать решения в единую цель. В завершение этого руководства мы обсуждаем составные списки – комбинции различных типов элементов и, кроме того, - пример разбора с помощью разностных списков.&lt;br /&gt;
&lt;br /&gt;
==Что такое Список?==&lt;br /&gt;
&lt;br /&gt;
В Прологе &amp;#039;&amp;#039;список (list)&amp;#039;&amp;#039; является объектом, содержащим внутри произвольное число других объектов. Списки соответствуют, грубо говоря, массивам в других языках, но, в отличие от массивов, список не трубует декларирования его размера до начала его использования.&lt;br /&gt;
&lt;br /&gt;
Список, содержащий числа 1, 2 и 3 записывается как &lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 3 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порядок элементов в этом списке значим:&lt;br /&gt;
*Число &amp;quot;1&amp;quot; является первым элементом,&lt;br /&gt;
*&amp;quot;2&amp;quot; - второй,&lt;br /&gt;
*&amp;quot;3&amp;quot; - третий.&lt;br /&gt;
&lt;br /&gt;
Список [ 1, 2, 3 ] и список [ 1, 3, 2 ] различны.&lt;br /&gt;
&lt;br /&gt;
Каждый компонент списка называется элемент (element). Для того, чтобы сформировать списковую структуру данных, следует разделять элементы запятыми и заключать их всех в квадратные скобки. Посмотрим на некоторые примеры:&lt;br /&gt;
&amp;lt;vip&amp;gt;[&amp;quot;dog&amp;quot;, &amp;quot;cat&amp;quot;, &amp;quot;canary&amp;quot;]&lt;br /&gt;
[&amp;quot;valerie ann&amp;quot;, &amp;quot;jennifer caitlin&amp;quot;, &amp;quot;benjamin thomas&amp;quot;]&amp;lt;/vip&amp;gt;&lt;br /&gt;
Один и тот же элемент может быть представлен в списке несколько раз, например:&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 1, 3, 1 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Объявление Списков===&lt;br /&gt;
Для объявления домена - списка целых используется декларация домена, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Звездочка означает &amp;quot;список этого&amp;quot;; то есть, integer* означает &amp;quot;список целых&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на то, что слово &amp;#039;&amp;#039;&amp;quot;list&amp;quot;&amp;#039;&amp;#039; не имеет специального значения в Visual Prolog. Вы равным образом могли бы назвать Ваш списковый домен как &amp;#039;&amp;#039;zanzibar&amp;#039;&amp;#039;. Именно звездочка, а не имя, предписывает этому домену быть списком.&lt;br /&gt;
&lt;br /&gt;
Элементами в списке может быть что угодно, включая другие списки. Но все элементы в списке должны принадлежать одному домену, и дополнительно к декларации спискового домена должна быть декларация &amp;#039;&amp;#039;&amp;#039;domains&amp;#039;&amp;#039;&amp;#039; для элементов:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;#039;&amp;#039;elements&amp;#039;&amp;#039; должны быть приравнены к простым доменным типам (например, integer, real или symbol) или к набору возможных альтернатив, обозначенных различными функторами. Visual Prolog не допускает смешивание стандартных типов в списке. Например, следующие декларации ошибочно представляют списки, созданные из integers, reals и symbols:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    integer;&lt;br /&gt;
    real;&lt;br /&gt;
    symbol.&lt;br /&gt;
        /* Неправильно */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходом для объявления списков из integer, real и symbols является объявление домена общего для всех типов, где функтор показывает какому типу принадлежит тот или иной элемент. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    i(integer);&lt;br /&gt;
    r(real);&lt;br /&gt;
    s(symbol).&lt;br /&gt;
        /* функторами являются i, r и s */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Подробнее об этом - в этом же руководстве в разделе &amp;quot;Составные списки&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
====Головы и Хвосты====&lt;br /&gt;
&lt;br /&gt;
Список на самом деле является рекурсивным составным объетом. Он состоит из двух частей - головы списка, которым является первый элемент, и хвоста - списка, который включает все следующие элементы.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Хвост списка всегда есть список; голова списка есть элемент.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Например,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;голова списка [a, b, c] есть a&lt;br /&gt;
хвост списка [a, b, c] есть [b, c]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Что происходит, когда мы имеем дело со списком, содержащим один элемент? Ответом является:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;головой списка [c] является c&lt;br /&gt;
хвостом списка [c] является []&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Если многократно отнимать первый элемент от хвоста списка, мы получим в конечном итоге пустой список (&amp;#039;&amp;#039;&amp;#039;[ ]&amp;#039;&amp;#039;&amp;#039;).&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Пустой список не может быть разбит на голову и хвост.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Это означает, что, концептуально говоря, списки имеют древовидную структуру подобно другим составным объектам. Древовидная структура списка [a, b, c, d] есть:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a    list&lt;br /&gt;
      /    \&lt;br /&gt;
     b    list&lt;br /&gt;
         /    \&lt;br /&gt;
        c    list&lt;br /&gt;
            /    \&lt;br /&gt;
           d      []&amp;lt;/pre&amp;gt;&lt;br /&gt;
Более того, одноэлементный список, такой как [a] - это не тот же самый элемент, который этот список содержит, поскольку [a] является действительно составной структурой данных, как это видно здесь:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a     []&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Представления Списков==&lt;br /&gt;
&lt;br /&gt;
Пролог содержит метод для явного обозначения головы и хвоста списка. Вместо разделения элементов запятыми можно отделять голову от хвоста вертикальной чертой (|). Например,&lt;br /&gt;
&lt;br /&gt;
[a, b, c] эквивалентно [a|[b, c]]&lt;br /&gt;
&lt;br /&gt;
и, продолжая процесс,&lt;br /&gt;
&lt;br /&gt;
[a|[b,c]] эквивалентно [a|[b|[c]]],&lt;br /&gt;
&lt;br /&gt;
что эквивалентно [a|[b|[c|[]]]]&lt;br /&gt;
&lt;br /&gt;
Можно даже использовать оба способа разделения в одном и том же списке, рассматривая вертикальную черту как разделитель самого низкого уровня. Следовательно, можно записать [a, b, c, d] как [a, b|[c, d]]. Таблица 1 дает дополнительные примеры.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 1: Головы и Хвосты списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список&lt;br /&gt;
!Голова&lt;br /&gt;
!Хвост&lt;br /&gt;
|-&lt;br /&gt;
|[&amp;#039;a&amp;#039;, &amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[&amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|-&lt;br /&gt;
|[ &amp;#039;a&amp;#039; ]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[] &lt;br /&gt;
|-&lt;br /&gt;
|/*пустой список*/ [ ]&lt;br /&gt;
|неопределен&lt;br /&gt;
|неопределен&lt;br /&gt;
|-&lt;br /&gt;
|[[1, 2, 3], [2, 3, 4], []]&lt;br /&gt;
|[1, 2, 3]&lt;br /&gt;
|[[2, 3, 4], []]&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
В Таблице 2 приведены некоторые примеры унификации списков.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 2: Унификация Списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список 1&lt;br /&gt;
!Список 2&lt;br /&gt;
!Связывание Переменных&lt;br /&gt;
|-&lt;br /&gt;
|[X, Y, Z]&lt;br /&gt;
|[эгберт, ест, мороженое]&lt;br /&gt;
|X=эгберт, Y=ест, Z=мороженое&lt;br /&gt;
|-&lt;br /&gt;
|[7]&lt;br /&gt;
|[X &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Y] &lt;br /&gt;
|X=7, Y=[]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2, 3, 4]&lt;br /&gt;
|[X, Y &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Z]&lt;br /&gt;
|X=1, Y=2, Z=[3,4]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2]&lt;br /&gt;
|[3 &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; X]&lt;br /&gt;
|fail&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Использование Списков==&lt;br /&gt;
&lt;br /&gt;
Поскольку списки являются в действительности рекурсивными составными структурами данных, для их обработки необходимы и рекурсивные алгоритмы. Самый естественный способ обработки списков - сквозной просмотр, в ходе которого что-то делается с каждым элементом, до тех пор, пока не достигнут конец.&lt;br /&gt;
&lt;br /&gt;
Как правило, такого рода алгоритмы используют два клауза. Один из них говорит о том, как поступать с обыкновенным списком, который может быть разделен на голову и хвост. Другой говорит о том, что делать с пустым списком.&lt;br /&gt;
&lt;br /&gt;
===Вывод Списков на печать===&lt;br /&gt;
&lt;br /&gt;
Например, если Вы хотите только вывести на печать элементы списка, то вот что Вы делаете:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  write_a_list : (integer*).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  write_a_list([]). /* Если список пустой, ничего не делаем. */&lt;br /&gt;
  write_a_list([H|T]):- /* Сопоставляем голову с H и хвост с T, и... */&lt;br /&gt;
    stdio::write(H),stdio::nl, /*выводим H и переводим строку*/&lt;br /&gt;
    write_a_list(T).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь мы видим два клауза write_a_list, которые можно выразить но обычном языке:&lt;br /&gt;
&lt;br /&gt;
*Для вывода на печать пустого списка ничего не надо делать.&lt;br /&gt;
*Иначе, для вывода на печать списка, вывести на печать его голову (она есть просто элемент), и потом вывести на печать хвост списка (он, как известно, есть список).&lt;br /&gt;
&lt;br /&gt;
Первый раз, когда вызывается:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такой вызов сопоставляется со вторым клаузом, с головой H=1 и T=[2, 3]. Это приводит к выводу на печать 1, затем рекурсивно вызывается write_a_list с аргументом в виде хвоста списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([2, 3]).&lt;br /&gt;
  /* Это вызов write_a_list(T). */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот второй вызов опять сопоставляется со вторым клаузом, где, на этот раз H=2 и T=[3], поэтому выводится 2 и опять рекурсивно вызывается write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С каким клаузом теперь такой вызов сопоставлятся? Напомним, что, хотя список [3] имеет всего один элемент, у него есть голова и хвост - голова есть 3, а хвост есть []. Таким образом, этот вызов опять сопоставляется со вторым клаузом с H=3 и T=[]. Теперь выводится 3 и  вызывается рекурсивно write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь становится понятно для чего нужен первый клауз. Второй клауз не может быть сопоставлен с таким вызовом, поскольку [] не может быть разделен на голову и хвост. Если бы первого клазуа здесь не было бы, то выполнение goal оказалось бы неуспешным. Но, поскольку он есть, то первый клауз сопоставляется с вызовом и выполнение goal успешно завершается и нечего более не делается.&lt;br /&gt;
&lt;br /&gt;
===Подсчет элементов в Списке===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь, как подсчитать число элементов в списке, или какова длина списка? Логично определить:&lt;br /&gt;
&lt;br /&gt;
*Длина пустого списка [] есть 0.&amp;lt;br /&amp;gt;&lt;br /&gt;
*Длина любого другого списка есть 1 плюс длина его хвоста.&lt;br /&gt;
&lt;br /&gt;
Можно ли это запрограммировать? На Прологе это очень просто. Всего два клауза:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], 0).&lt;br /&gt;
  length_of([_|T], L):-&lt;br /&gt;
    length_of(T, TailLength),&lt;br /&gt;
    L = TailLength + 1.&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L),&lt;br /&gt;
  stdio::write(L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотрите прежде всего на второй клауз. Строго говоря, [_|T] сопоставляется с любым непустым списком, связывая T с хвостом списка. Значение головы неважно, если она есть, она может быть учтена как один элемент.&lt;br /&gt;
&lt;br /&gt;
Тогда вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется со вторым клаузом, с T=[2, 3]. Следующим шагом является вычисление длины хвоста T. Когда это сделано (не имеет значение, как), TailLength получит значение 2, и компьютер теперь может добавить 1 к ней и связать L со значением 3. Как выполняется этот промежуточный шаг? Надо найти длину списка [2, 3], путем удовлетворения цели&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([2, 3], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Другими словами, length_of вызывает себя рекурсивно. Этот вызов сопоставляется со вторым клаузом, связывая&lt;br /&gt;
&lt;br /&gt;
*[3] и T в вызове клаузы и&lt;br /&gt;
*TailLength с L в клаузе.&lt;br /&gt;
&lt;br /&gt;
Подчеркиваем, TailLength в вызове никак не пересекается с TailLength в клаузе, поскольку &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;каждый рекурсивный вызов клауза имеет собственный набор переменных&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Итак, теперь задача - найти длину списка [3], которая есть 1, и мы добавляем 1 к этому значению, чтобы получить длину списка [2, 3], что будет 2. Ну и хорошо!.&lt;br /&gt;
&lt;br /&gt;
Аналогично, length_of вызывает себя рекурсивно опять для получения длины списка [3]. Хвост  [3] есть [], поэтому T связвается с [], и задача теперь - получение длины списка [] и добавление к ней 1, что дает длину списка [3].&lt;br /&gt;
&lt;br /&gt;
Теперь все просто. Цель:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется с &amp;#039;&amp;#039;первым&amp;#039;&amp;#039; клаузом, связывая TailLength с 0. Поэтому теперь компьютер может добавить 1 к нему, получая длину списка [3], и возвращаясь теперь в вызывавший клауз. Это, в свою очередь, опять добавляет 1, давая длину списка [2, 3], и возвращается в клауз, который его вызывал; этот первоначальный клауз добавит снова 1, давая длину списка [1, 2, 3].&lt;br /&gt;
&lt;br /&gt;
Не растерялись? Мы надеемся, нет. В следующей короткой иллюстрации мы сводим воедино все вызовы. Мы использовали здесь прием подстрочника для того, чтобы показать, что аналогично называемые переменные в разных клаузах или различные вызовы того же самого клауза - одно и то же. &lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L1).&lt;br /&gt;
my::length_of([2, 3], L2).&lt;br /&gt;
my::length_of([3], L3).&lt;br /&gt;
my::length_of([], 0).&lt;br /&gt;
L3 =  0+1 = 1.&lt;br /&gt;
L2 = L3+1 = 2.&lt;br /&gt;
L1 = L2+1 = 3.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что Вам не нужно каждый раз создавать такого рода предикаты самостоятельно, Вы можете использовать готовый предикат &amp;#039;&amp;#039;&amp;#039;list::length&amp;#039;&amp;#039;&amp;#039; из PFC.&lt;br /&gt;
&lt;br /&gt;
===Хвостовая рекурсия===&lt;br /&gt;
&lt;br /&gt;
Вы, очевидно, заметили, что length_of не является (и не может быть) предикатом с хвостовой рекурсией, поскольку рекурсивный вызов не является последним шагом в его клаузе. Возможно ли создать предикат, определяющий длину, так, чтобы он был предикатом с хвостовой рекурсией? Да, но это потребует некоторых усилий.&lt;br /&gt;
&lt;br /&gt;
Проблема с предикатом length_of в том, что длину списка нельзя вычислить до тех пор, пока не вычислена длина его хвоста. Но из этой ситуации есть выход. Нам потребуется предикат, вычисляющий длину списка, с тремя аргументами.&lt;br /&gt;
&lt;br /&gt;
*Один из них - это список, от которого компьютер будет откусывать по одному элементу на каждом вызове до тех пор, пока этот список, как и прежде, не превратится в пустой список.&lt;br /&gt;
*Второй - это свободный аргумент, который в конечном итоге вернет результат (длину).&lt;br /&gt;
*Третий - это счетчик, значение которого начинается с нуля и увеличивается с каждым вызовом.&lt;br /&gt;
&lt;br /&gt;
Когда список в конечном итоге станет пустым, мы проунифицируем счетчик с несвязанным результатом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer, integer) procedure(i,o,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], Result, Result).&lt;br /&gt;
  length_of([_|T], Result, Counter):-&lt;br /&gt;
    NewCounter = Counter + 1,&lt;br /&gt;
    length_of(T, Result, NewCounter).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L, 0), /* Начинаем со счетчиком Counter = 0 */&lt;br /&gt;
  stdio::write(&amp;quot; L = &amp;quot;, L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта версия предиката length_of более сложная и во многих смыслах менее логичная, чем предыдущая. Мы ее представили здесь главным образом для того, чтобы показать, что на практике &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;вы можете часто построить алгоритм с хвостовой рекурсией для задач, которые на первый взгляд требуют рекурсии другого типа&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
===Модификация Списка===&lt;br /&gt;
&lt;br /&gt;
Иногда требуется создать другой список из заданного списка. Это делается путем просмотра списка, элемент за элементом, заменяя каждый элемент вычисленным значением. Например, как эта программа, которая добавляет 1 к каждому элементу исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  add1 : (integer*, integer*) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  add1([], [])./* граничное условие */&lt;br /&gt;
  add1([Head|Tail],[Head1|Tail1]):- /* отделяем голову от остального списка*/&lt;br /&gt;
    Head1 = Head+1, /* добавляем 1 к элементу-голове */&lt;br /&gt;
    add1(Tail, Tail1)./* далаем это с остальной частью списка*/&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::add1([1,2,3,4], NewList),&lt;br /&gt;
  stdio::write(NewList)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На обычном языке это звучит так:&lt;br /&gt;
*Добавление 1 ко всем элементам пустого списка порождаем пустой список,&lt;br /&gt;
*Для добавления 1 ко всем элемента любого другого списка:&lt;br /&gt;
**добавить 1 к голове и сделать эту голову головой результирующего списка, а затем&lt;br /&gt;
**добавить 1 к каждому элемента хвоста и этот хвост сделать хвостом результата.&lt;br /&gt;
&lt;br /&gt;
Загрузим программу и выполним такую цель&lt;br /&gt;
&amp;lt;vip&amp;gt;add1([1,2,3,4], NewList).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель вернет&lt;br /&gt;
&amp;lt;vip&amp;gt;NewList=[2,3,4,5]&lt;br /&gt;
1 Solution&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Опять о хвостовой рекурсии===&lt;br /&gt;
&lt;br /&gt;
Является ли предикат add1 проедикатом с хвостовой рекурсией? &lt;br /&gt;
Если у Вас есть опыт использования Lisp или Pascal, Вы могли бы подумать, что нет, поскольку Вы бы рассуждали так:&lt;br /&gt;
&lt;br /&gt;
*Делим список на &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;.&lt;br /&gt;
*Добавляем 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавляя 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
*Соединяем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, что дает результирующий список.&lt;br /&gt;
&lt;br /&gt;
Это не похоже на хвостовую рекурсию, поскольку последний шаг - не рекурсивный вызов.&lt;br /&gt;
&lt;br /&gt;
Однако, и это важно, – &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Это не то, что делает Пролог&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;. В Visual Prolog add1 является предикатом с хвостовой рекурсией, поскольку выполняется в действительности следующим образом:&lt;br /&gt;
&lt;br /&gt;
*Связать голову и хвост исходного списка с &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, соответственно.&lt;br /&gt;
*Связать голову и хвост результирующего списка с &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, соответственно. (&amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; пока не получили значений.)&lt;br /&gt;
*Добавить 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавить 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Когда это сделано, &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;уже являются&amp;#039;&amp;#039;&amp;#039; головой и списком результата и отдельной операции по их соединению нет. Поэтому рекурсивный вызов и является последним шагом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h6&amp;gt;Снова Модификация Списков&amp;lt;/h6&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Конечно, не всегда модификации подлежит каждый элемент. Посмотрим на программу, которая сканирует список чисел и копирует его, удаляя отрицательные числа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  discard_negatives : (integer*, integer*) procedure(i,o). /*удалить отрицательные*/&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  discard_negatives([], []).&lt;br /&gt;
  discard_negatives([H|T], ProcessedTail):-&lt;br /&gt;
    H &amp;lt; 0,&lt;br /&gt;
    !, /* Если H отрицательно, пропускаем его */&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
  discard_negatives([H|T], [H|ProcessedTail]):-&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::discard_negatives ([2, -45, 3, 468], X),&lt;br /&gt;
  stdio::write(X).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напрмер, цель&lt;br /&gt;
&amp;lt;vip&amp;gt;my::discard_negatives([2, -45, 3, 468], X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
дает&lt;br /&gt;
&amp;lt;vip&amp;gt;X=[2, 3, 468].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
А вот - предикат который копирует элементы списка, добавляя для каждого элемента его дубликат:&lt;br /&gt;
&amp;lt;vip&amp;gt;doubletalk([], []).&lt;br /&gt;
doubletalk([H|T], [H, H|DoubledTail]) :-&lt;br /&gt;
  doubletalk(T, DoubledTail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Принадлежнось списку===&lt;br /&gt;
&lt;br /&gt;
Допустим, имеется список с именами &amp;#039;&amp;#039;John&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Leonard&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Eric&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Frank&amp;#039;&amp;#039; и требуется, используя Visual Prolog, выяснить, принадлежит ли заданное имя этому списку. Другими словами, надо определить &amp;quot;отношение&amp;quot; между двумя аргументами: именем и списком имен. Это соответствует предикату&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember : (name, name*).&lt;br /&gt;
  /* &amp;quot;name&amp;quot; принадлежит списку &amp;quot;name*&amp;quot; */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В программе e01.pro первый клауз исследует голову списка. Если голова списка совпадает с искомым именем, то можно сделать заключение, что Name принадлежит списку. Поскольку хвост списка нас не интересует, то это мы представляем анонимной переменной. Благодаря первому клаузу, цель&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;])&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
удовлетворена.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Программа e01.pro */&lt;br /&gt;
class my&lt;br /&gt;
predicates&lt;br /&gt;
  isMember : (A, A*) determ.&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  isMember(Name, [Name|_]) :-&lt;br /&gt;
    !.&lt;br /&gt;
  isMember(Name, [_|Tail]):-&lt;br /&gt;
    isMember(Name,Tail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;]),&lt;br /&gt;
  !,&lt;br /&gt;
  stdio::write(&amp;quot;Success&amp;quot;)&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если голова списка не есть Name, то надо исследовать, не содержится ли Name в хвосте списка.&lt;br /&gt;
&lt;br /&gt;
На обычном языке:&lt;br /&gt;
&lt;br /&gt;
Name принадлежит списку, если Name является первым элементом списка, или&amp;lt;br /&amp;gt;&lt;br /&gt;
Name принадлежит списку, если Name принадлежит хвосту.&lt;br /&gt;
&lt;br /&gt;
Второй клауз предиката isMember относится к этому отношению. Таким образом на Visual Prolog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember(Name, [_|Tail]) :-&lt;br /&gt;
    isMember(Name, Tail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Добавление списка к другому списку: декларативное и рекурсивное решения===&lt;br /&gt;
&lt;br /&gt;
Рассмотренный предикат member программы e01.pro работает в двух направлениях. Вернемся к его клаузам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
member(Name, [Name|_]).&lt;br /&gt;
member(Name, [_|Tail]) :-&lt;br /&gt;
  member(Name, Tail).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На эти клаузы можно смотреть с двух различных точек зрения: декларативной и процедурной.&lt;br /&gt;
&lt;br /&gt;
*С декларативной точки зрения, клаузы выражают: &lt;br /&gt;
**Name (Имя) принадлежит списку, если голова списка есть Name&lt;br /&gt;
**иначе Name принадлежит списку, если оно (Имя) принадлежит хвосту.&lt;br /&gt;
*С процедурной точки зрения, эти же два клауза могут быть интерпретированы так:&amp;lt;br/&amp;gt;Чтобы найти элемент списка &lt;br /&gt;
**Найдите его голову&lt;br /&gt;
**иначе найдите элемент хвоста списка.&lt;br /&gt;
&lt;br /&gt;
Эти две точки зрения соответствуют целям&lt;br /&gt;
&amp;lt;vip&amp;gt;member(2, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;member(X, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате первый вызов поручает Visual Prolog(у) проверить, истино ли нечто (принадлежность числа 2 списку [1,2,3,4]). Второй вызов поручает Visual Prolog(у) найти все члены списка [1,2,3,4]. Не смущайтесь этим. Предикат member является одним и тем же, но на его поведение можно смотреть под разными углами.&lt;br /&gt;
&lt;br /&gt;
====Рекурсия с процедуральной точки зрения====&lt;br /&gt;
&lt;br /&gt;
Прелесть Пролога заключается в том, что часто, когда мы конструируем клаузы для предиката, будучи на одной точке зрения, они будут работать и при взгляде с другой точки зрения. Чтобы обнаружить эту дуальность, мы приведем пример предиката для добавления (append) одного списка к другому. Мы определяем предикат append с тремя аргументами:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append(List1, List2, List3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот предикат интегрирует списки List1 и List2 в форму списка List3 так, что список List2 дописывается в конце списка List1. То есть содержательно - осуществляется добавление списка List2 к списку LIst1. Опять мы используем рекурсию (на этот раз с процедуральной точки зрения).&lt;br /&gt;
&lt;br /&gt;
Если список List1 пустой, результатом добавления списка List1 к списку List2 будет тот же самый List2. Запишем это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([], List2, List2).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если список List1 не пустой, то можно преобразовать списки List1 и List2 к форме списка &amp;#039;&amp;#039;List3&amp;#039;&amp;#039;, сделав голову списка List1 головой списка List3. В приведенном коде переменная H используется в качестве головы как списка List1, так и списка List3. хвост списка List3 есть список L3, который составлен из остатка списка List1 (а именно, L1) и всего списка List2. Опять выразим это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([H|L1], List2, [H|L3]) :-&lt;br /&gt;
   append(L1, List2, L3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append работает следующим образом: пока список List1 не пустой, рекурсивное правило дописывает один элемент каждый раз к списку List3. Когда список List1 становится пустым, первый клауз  clause обеспечивает дописывание списка List2 в конец списка List3.&lt;br /&gt;
&lt;br /&gt;
====Варианты использования одного предиката====&lt;br /&gt;
&lt;br /&gt;
Подходя к предикату append с декларативной точки зрения, мы определили его как отношение между тремя списками. Это отношение справедливо также, если списки List1 и List3 известны, а List2 - нет. Более того, это также работает, если только List3 известен. Например, для того, чтобы выяснить, какие два списка могли бы быть соединены для получения известного списка, можно использовать вызов в форме&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, L2, [1, 2, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С таким целевым вызовом, Visual Prolog найдет следующие решения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
L1=[], L2=[1,2,4]&lt;br /&gt;
L1=[1], L2=[2,4]&lt;br /&gt;
L1=[1,2], L2=[4]&lt;br /&gt;
L1=[1,2,4], L2=[]&lt;br /&gt;
4 Solutions&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно использовать предикат append для нахождения списка, который следовало бы добавить к списку [3,4] для получения списка[1,2,3,4]. Попробуем такой вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, [3,4], [1,2,3,4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Visual Prolog находит решение&lt;br /&gt;
&amp;lt;vip&amp;gt;L1=[1,2].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append определяет отношение между &amp;#039;&amp;#039;входным набором (input set)&amp;#039;&amp;#039; и &amp;#039;&amp;#039;выходным набором (output set)&amp;#039;&amp;#039; таким образом, что отношение применимо в обе стороны. При таком отношении возникает вопрос&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Что является выходным набором для заданного входного?&amp;#039;&amp;#039; или &amp;#039;&amp;#039;Какой входной набор соответствует заданному выходному?&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Статус аргументов данного вызова предиката известен как &amp;#039;&amp;#039;поток (или шаблон) ввода-вывода&amp;#039;&amp;#039;. Аргумент, который связан или наследуется в момент вызова является входным аргументом и обозначается как (i). Свободный аргумент является выходным аргументом и обозначается как (o).&lt;br /&gt;
&lt;br /&gt;
Предикат append обладает свойством поддерживать любой шаблон ввода-вывода, какой требуется. Однако не все предикаты имеют возможность вызова с различными шаблонами ввода-вывода. Когда клауз Пролога способен поддерживать множество шаблонов ввода-вывода, он называется инверсным клаузом. Целый набор предикатов для обработки списков содержится в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Все Решения Сразу==&lt;br /&gt;
&lt;br /&gt;
Откаты и рекурсии являются двумя способами выполнения повторяющихся процессов. Рекурсия предпочтительнее, поскольку, в отличие от отката, позволяет передать данные через аргументы от одного рекурсивного вызова к следующему. Благодаря этому, рекурсивные процедуры могут использовать промежуточные результаты или счетчики по ходу выполнения.&lt;br /&gt;
&lt;br /&gt;
Однако есть одна вещь, которую, в отличие от рекурсии, могут делать откаты, а именно – искать все альтернативные решения посредством одного вызова. Поэтому Вы можете оказаться в затруднительном положении: Вам нужно получить все решения за один вызов, и, при этом, они Вам нужны все сразу, как часть единой интегрированной структуры данных. Что делать?&lt;br /&gt;
&lt;br /&gt;
К счастью, Visual Prolog позволяет найти выход из этого положения. Предопределенная конструкция обработки списков получает целевой вызов в качестве своего аргумента и собирает все решения для этого вызова в единый выходной список. Такая конструкция для списков имеет два аргумента:&lt;br /&gt;
&lt;br /&gt;
*Первый аргумент, VarName, определяет аргумент в вызываемом целевом предикате, значения которого будут собираться в список.&lt;br /&gt;
*Второй - mypredicate - определяет предикат, который будет получать значения.&lt;br /&gt;
*Выходной параметр ListParam, является переменной, которая содержит список значений, полученных в ходе отката.&lt;br /&gt;
&lt;br /&gt;
Программа e02.pro использует обработку списка для вывода среднего возраста группы людей.&lt;br /&gt;
&amp;lt;vip&amp;gt;        /* Программа e02.pro */&lt;br /&gt;
class my&lt;br /&gt;
domains&lt;br /&gt;
  name = string.&lt;br /&gt;
  address = string.&lt;br /&gt;
  age = integer.&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  person : (name, address, age) nondeterm anyflow.&lt;br /&gt;
  sumlist : (age*, age, integer)procedure(i,o,o).&lt;br /&gt;
end class my&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  sumlist([],0,0).&lt;br /&gt;
  sumlist([H|T], Sum, N):-&lt;br /&gt;
    sumlist(T, S1, N1),&lt;br /&gt;
    Sum=H+S1, N=1+N1.&lt;br /&gt;
  &lt;br /&gt;
  person(&amp;quot;Sherlock Holmes&amp;quot;,&amp;quot;22B Baker Street&amp;quot;, 42).&lt;br /&gt;
  person(&amp;quot;Pete Spiers&amp;quot;,&amp;quot;Apt. 22, 21st Street&amp;quot;, 36).&lt;br /&gt;
  person(&amp;quot;Mary Darrow&amp;quot;,&amp;quot;Suite 2, Omega Home&amp;quot;, 51).&lt;br /&gt;
end implement my&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  L = [ Age || my::person(_, _, Age)],&lt;br /&gt;
  my::sumlist(L, Sum, N),&lt;br /&gt;
  Ave = Sum/N,&lt;br /&gt;
  stdio::write(&amp;quot;Average=&amp;quot;, Ave, &amp;quot;. &amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Клауз обработки списка в этой программе создает список L, который является списком всех возрастов, полученных от предиката person. Если бы потребовалось бы собрать список всех людей в возрасте 42 лет, можно было бы задать следующий цель-вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ Who || my::person(Who, _, 42) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий код порождает список всех положительных чисел исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ X || X = list::getMember_nd([2,-8,-3,6]), X &amp;gt; 0]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Смешанные списки==&lt;br /&gt;
&lt;br /&gt;
Список целых объявляется просто&lt;br /&gt;
&amp;lt;vip&amp;gt;integer*&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же верно и для списков вещественных чисел (integer), символьных (symbol) списков или списков строк (string).&lt;br /&gt;
&lt;br /&gt;
Однако часто приходится хранить комбинацию различных типов элементов в составе одного и того же списка, такую как:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[2, 3, 5.12, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot;].&lt;br /&gt;
        /* Не корректно для Visual Prolog*/&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Смешанные списки&amp;#039;&amp;#039; - списки, содержащие элементы более чем одного типа. Для работы со списками разнотипных элементов должны использоваться специальные объявления, поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;одному и тому же&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; домену. Способом создания списка, который хранит такие различные типы элементов является использование функторов, поскольку домен может представляться &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;более, чем одним&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; функтором, каждый со своими т&amp;#039;&amp;#039;&amp;#039;и&amp;#039;&amp;#039;&amp;#039;повыми аргументами.&lt;br /&gt;
&lt;br /&gt;
Пример объаявления списка, который может хранить целые, символы, строки или списки таких  даных:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  /* функторами являются l, i, c, and s */&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Список&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 2, 9, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot; ]&lt;br /&gt;
/* Не корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
на Visual Prolog следовало бы записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[i(2), i(9), l([s(&amp;quot;food&amp;quot;), s(&amp;quot;goo&amp;quot;)]), s(&amp;quot;new&amp;quot;)]&lt;br /&gt;
        /* Корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример предиката append показывает как использовать такого рода декларации в стандартных программах манипулирования списками.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  append : (A*,A*,A*) procedure (i,i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  append([], L, L).&lt;br /&gt;
  append([X|L1], L2, [X|L3]):-&lt;br /&gt;
    append(L1, L2, L3).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::s(&amp;quot;likes&amp;quot;),my::l([my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;mary&amp;quot;)])],&lt;br /&gt;
    [my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;sue&amp;quot;)], &lt;br /&gt;
    Ans&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Первый список: &amp;quot;, Ans,&amp;quot;\n\n&amp;quot;),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::l([my::s(&amp;quot;This&amp;quot;),my::s(&amp;quot;is&amp;quot;),my::s(&amp;quot;a&amp;quot;),my::s(&amp;quot;list&amp;quot;)]),my::s(&amp;quot;bee&amp;quot;)], &lt;br /&gt;
    [my::c(&amp;#039;c&amp;#039;)], &lt;br /&gt;
    Ans2&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Второй списко: &amp;quot;, Ans2, &amp;quot;\n\n&amp;amp;quot).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Разбор с использованием списков==&lt;br /&gt;
&lt;br /&gt;
Ниже приведена программа, демонстрирующая [[wikipedia:parsing|разбор]] (parsing)с использованием списков. Процесс разбора работает в методом сворачивания. В этом примере входная строка преобразуется в структуру данных Пролога, которая может быть использована далее.&lt;br /&gt;
&lt;br /&gt;
Этот разборщик предназначен для примитивного формального языка. Хотя этот пример достаточно сложен с точки зрения этого руководства, мы решили поместит его здесь, поскольку разбор является одной из областей, где Visual Prolog очень эффективен. Если Вы чувствуете себя не вполне готовым для этого раздела, Вы можете этот пример пропустить и продолжить чтение руководства без потери содержательности.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;#include @&amp;quot;pfc\exception\exception.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\string\string.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\console\console.ph&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class my_t&lt;br /&gt;
predicates&lt;br /&gt;
  tokl : (string, string*) procedure (i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_t&lt;br /&gt;
clauses&lt;br /&gt;
  tokl(Str, [H|T]) :-&lt;br /&gt;
    string::fronttoken(Str, H, Str1),&lt;br /&gt;
    !,&lt;br /&gt;
    tokl(Str1, T).&lt;br /&gt;
  tokl(_, []).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
/* * * * * * * * * * * * * * * * * * * *  *&lt;br /&gt;
* Вторая часть этой программы - разборщик *&lt;br /&gt;
* * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
class my_p&lt;br /&gt;
domains&lt;br /&gt;
  program = program(statement*).&lt;br /&gt;
  /* * * * * * * * * * * * * * * * * * * * *  * &lt;br /&gt;
  * Определение что есть языковая конструкция *&lt;br /&gt;
  * (предложение языка)                       *&lt;br /&gt;
  * * * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
  statement =&lt;br /&gt;
    if_Then_Else(exp, statement, statement);&lt;br /&gt;
    if_Then(exp, statement);&lt;br /&gt;
    while(exp, statement);&lt;br /&gt;
    assign(id, exp).&lt;br /&gt;
  /* * * * * * * * * * *  *&lt;br /&gt;
  * Определение выражения *&lt;br /&gt;
  * * * * * * * *  * * *  */&lt;br /&gt;
  exp = &lt;br /&gt;
    plus(exp, exp);&lt;br /&gt;
    minus(exp, exp);&lt;br /&gt;
    var(id);&lt;br /&gt;
    int(integer).&lt;br /&gt;
    id = string.&lt;br /&gt;
&lt;br /&gt;
  predicates&lt;br /&gt;
    s_program : (string*, program) procedure (i,o).&lt;br /&gt;
    s_statement : (string*, string*, statement) determ (i,o,o).&lt;br /&gt;
    s_statement_list : (string*, string*, statement*) determ (i,o,o).&lt;br /&gt;
    s_exp : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
    s_exp1 : (string*, string*, exp, exp) determ (i,o,i,o).&lt;br /&gt;
    s_exp2 : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_p&lt;br /&gt;
clauses&lt;br /&gt;
  s_program(TokenList, program(StatementList)):-&lt;br /&gt;
    s_statement_list(TokenList, _, StatementList),&lt;br /&gt;
    !.&lt;br /&gt;
  s_program(_, program([])).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement_list([], [], []) :- !.&lt;br /&gt;
  s_statement_list(List1, List4, [Statement|Program]) :-&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;;&amp;quot;|List3],&lt;br /&gt;
    s_statement_list(List3, List4, Program).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List7,if_then_else(Exp,Statement1, Statement2)):-&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement1),&lt;br /&gt;
    List4=[&amp;quot;else&amp;quot;|List5],!,&lt;br /&gt;
    s_statement(List5, List6, Statement2),&lt;br /&gt;
    List6=[&amp;quot;fi&amp;quot;|List7].&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List5,if_then(Exp, Statement)) :- !,&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement),&lt;br /&gt;
    List4=[&amp;quot;fi&amp;quot;|List5].&lt;br /&gt;
  s_statement([&amp;quot;do&amp;quot;|List1], List4,while(Exp, Statement)) :- !,&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;while&amp;quot;|List3],&lt;br /&gt;
    s_exp(List3, List4, Exp).&lt;br /&gt;
  s_statement([ID|List1], List3,assign(Id,Exp)) :-&lt;br /&gt;
    string::isname(ID),&lt;br /&gt;
    List1=[&amp;quot;=&amp;quot;|List2],&lt;br /&gt;
    s_exp(List2, List3, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp(List1, List3, Exp):-&lt;br /&gt;
    s_exp2(List1, List2, Exp1),&lt;br /&gt;
    s_exp1(List2, List3, Exp1, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp1([&amp;quot;+&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, plus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1([&amp;quot;-&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, minus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1(List, List, Exp, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp2([Int|Rest], Rest, int(I)) :-&lt;br /&gt;
    trap(I = toTerm(Int),Error,exception::clear_fail(Error)),&lt;br /&gt;
    !.&lt;br /&gt;
  s_exp2([Id|Rest], Rest, var(Id)) :-&lt;br /&gt;
    string::isname(Id).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  stdio::write(Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res),&lt;br /&gt;
  stdio::write(Res).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Загрузите программу и выполните целевой вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;goal&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Prolog вернет структуру программы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;Ans = [&amp;quot;b&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;if&amp;quot;,&amp;quot;b&amp;quot;,&amp;quot;then&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;1&amp;quot;,&lt;br /&gt;
    &amp;quot;else&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;fi&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;do&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;a&amp;quot;,&lt;br /&gt;
    &amp;quot;-&amp;quot;,&amp;quot;1&amp;quot;,&amp;quot;while&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;;&amp;quot;],&lt;br /&gt;
Res=program&lt;br /&gt;
  (&lt;br /&gt;
  [assign(&amp;quot;b&amp;quot;,int(2)),&lt;br /&gt;
   if_then_else&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;b&amp;quot;),&lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(1)), &lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(2))&lt;br /&gt;
     ),&lt;br /&gt;
   while&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;a&amp;quot;),&lt;br /&gt;
     assign&lt;br /&gt;
       (&lt;br /&gt;
       &amp;quot;a&amp;quot;,&lt;br /&gt;
       minus&lt;br /&gt;
         (&lt;br /&gt;
         var(&amp;quot;a&amp;quot;),&lt;br /&gt;
         int(1)&lt;br /&gt;
         )&lt;br /&gt;
       )&lt;br /&gt;
     )&lt;br /&gt;
  ])&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Преобразование в этом примере представлено двумя шагами: сканирование и разбор. Предикат tokl  - это сканер. Он принимает строку и преобразует ее в список токенов. Все предикаты с именами, начинающимися с s_ являются предикатами парсера. В этом примере входный текст представляет программу на языке, подобном языку Паскаль, и состоящей из Паскале-подобных предложений. Этот язык программирования позволяет только предложения вида: IF THEN ELSE, IF THEN, DO WHILE и присваивание (ASSIGNMENT). Предложения состоят из выражений и вложенных предложений. Выражения - сложение, вычитание, переменные и целые.&lt;br /&gt;
&lt;br /&gt;
Далее, как это работает:&lt;br /&gt;
&lt;br /&gt;
*Первый клауз парсера, s_program, принимает список токенов и пытается преобразовать его в список предложений.&lt;br /&gt;
*Предикат s_statement_list принимает тот же самый список токенов и проверяет возможность деления токенов на предложения, завершающиеся точкой с запятой.&lt;br /&gt;
*Предикат s_statement проверяет могут ли начальные токены списка (токенов) представлять собой правильное предложение. Если да, то такое предложение возвращается в виде структуры, а остаток токенов передается рекурсивно вызываемому предикату s_statement_list.&lt;br /&gt;
*Четыре клауза предиката s_statement соответствуют четырем типам, которые парсер понимает.&amp;lt;br\&amp;gt; Если первый клауз предиката s_statement не может преобразовать список токенов в предложение вида IF THEN ELSE, то клауз завершается неуспешно и в порядке отката переходит к следующему клаузу предиката s_statement. Теперь делается попытка преобразовать список токенов в конструкцию вида IF THEN. Если эта попытка неуспешна, то в следующем клаузе делается попытка преобразования к предложению вида DO WHILE.&lt;br /&gt;
*Если первые три клауза предиката s_statement завершаются неуспешно, то последний клауз этого предиката провереяет представлена ли в списке токенов операция присваивания. Этот клауз проверяет &amp;quot;на присваивание&amp;quot; является ли первый терм символом, второй - знаком равно (&amp;quot;=&amp;quot;), а следующие термы представляют простое математическое выражение.&lt;br /&gt;
*Предикаты s_exp, s_exp1 и s_exp2 работают аналогично, путем проверки являются ли начальные термы выражениями и, если это так, то предикату s_statement предикат s_exp возвращает остаток термов и математическое выражение в виде структуры.&lt;br /&gt;
&lt;br /&gt;
==Заключение==&lt;br /&gt;
&lt;br /&gt;
В этом руководстве раскрыта следующие важные моменты:&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;Списки&amp;#039;&amp;#039; могут содержать произвольное число элементов; Вы объявляете их простым добавление звездочки (&amp;#039;&amp;#039;&amp;#039;*&amp;#039;&amp;#039;&amp;#039;) в конце ранее определенного домена.&lt;br /&gt;
*Список является рекурсивным составным объектом, состоящим из головы и хвоста. Голова есть первый элемент, а хвост - остальная часть списка (без первого элемента). Хвост списка - всегда список; голова списка - всегда элемента. Список может содержать ноль или более элементов; Пустой список обозначается [].&lt;br /&gt;
*Элементами списка можже быть что угодно, включая другиме списки; все элементы списка должны принадлежать одному домену. В этом случае объявление домена элементов должно выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где elements = один из стандартных доменов (integer, real, etc.) или набор альтернатив  обозначенных различными функторами (int(integer); rl(real); smb(symbol); и т.д.). Смешение типов в списках языка системы Visual Prolog допускается только включением их в составные объекты или функторы.&lt;br /&gt;
*Можно использовать разделители (запятые, [ и |) для явного отделения головы списка от хвоста. Так, список&lt;br /&gt;
&amp;lt;vip&amp;gt;[a, b, c, d]&amp;lt;/vip&amp;gt;&lt;br /&gt;
может быть записан как:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
[a|[b, c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b|[c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b, c|[d]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c, d]]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c|[d]]]] &lt;br /&gt;
или даже&lt;br /&gt;
[a|[b|[c|[d|[]]]]]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Обработка списков заключается в рекурсивном отщеплении головы списка (и выполнении действий над ней) до опустошения списка.&lt;br /&gt;
*Предикаты для работы со списками содержаться в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
*Visual Prolog поддерживает встроенную конструкцию обработки списков, которая принимает целевой предикат в качестве одного из аргументов и собирает все решения этого целевого предиката в едином списке. Синтаксис такой конструкции&lt;br /&gt;
&amp;lt;vip&amp;gt;Result = [ Argument || myPredicate(Argument) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы одному и тому же домену, следует использовать функторы для создания списков, хранящих элементы различного типа.&lt;br /&gt;
*процесс &amp;#039;&amp;#039;разбора с использованием разностных списков (parsing by difference lists)&amp;#039;&amp;#039; работает путем сокращения задачи; пример в этом руководстве преобрахует строку входных данных в структуру, которая может обрабатываться позже.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[[en:Lists and Recursion]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1590</id>
		<title>Списки и рекурсия</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1590"/>
		<updated>2007-11-23T10:57:49Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: очепятки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Обработка списков как обработка последовательности элементов - это мощная техника, используемая в Прологе. В этом руководстве мы рассматриваем что такое списки, как их объявлять, и затем приводим несколько примеров, показывающих как использовать работу со списками в приложениях. Мы также определяем два хорошо известных предиката Пролога – member (член, элемент) и append (добавить) – рассматривая обработку списков как с рекурсивной, так и процедурной точки зрения.&lt;br /&gt;
&lt;br /&gt;
Затем мы представляем предикат findall - стандартный предикат языка системы Visual Prolog, позволяющий собирать решения в единую цель. В завершение этого руководства мы обсуждаем составные списки – комбинции различных типов элементов и, кроме того, - пример разбора с помощью разностных списков.&lt;br /&gt;
&lt;br /&gt;
==Что такое Список?==&lt;br /&gt;
&lt;br /&gt;
В Прологе &amp;#039;&amp;#039;список (list)&amp;#039;&amp;#039; является объектом, содержащим внутри произвольное число других объектов. Списки соответствуют, грубо говоря, массивам в других языках, но, в отличие от массивов, список не трубует декларирования его размера до начала его использования.&lt;br /&gt;
&lt;br /&gt;
Список, содержащий числа 1, 2 и 3 записывается как &lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 3 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порядок элементов в этом списке значим:&lt;br /&gt;
*Число &amp;quot;1&amp;quot; является первым элементом,&lt;br /&gt;
*&amp;quot;2&amp;quot; - второй,&lt;br /&gt;
*&amp;quot;3&amp;quot; - третий.&lt;br /&gt;
&lt;br /&gt;
Список [ 1, 2, 3 ] и список [ 1, 3, 2 ] различны.&lt;br /&gt;
&lt;br /&gt;
Каждый компонент списка называется элемент (element). Для того, чтобы сформировать списковую структуру данных, следует разделять элементы запятыми и заключать их всех в квадратные скобки. Посмотрим на некоторые примеры:&lt;br /&gt;
&amp;lt;vip&amp;gt;[&amp;quot;dog&amp;quot;, &amp;quot;cat&amp;quot;, &amp;quot;canary&amp;quot;]&lt;br /&gt;
[&amp;quot;valerie ann&amp;quot;, &amp;quot;jennifer caitlin&amp;quot;, &amp;quot;benjamin thomas&amp;quot;]&amp;lt;/vip&amp;gt;&lt;br /&gt;
Один и тот же элемент может быть представлен в списке несколько раз, например:&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 1, 3, 1 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Объявление Списков===&lt;br /&gt;
Для объявления домена - списка целых используется декларация домена, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Звездочка означает &amp;quot;список этого&amp;quot;; то есть, integer* означает &amp;quot;список целых&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на то, что слово &amp;#039;&amp;#039;&amp;quot;list&amp;quot;&amp;#039;&amp;#039; не имеет специального значения в Visual Prolog. Вы равным образом могли бы назвать Ваш списковый домен как &amp;#039;&amp;#039;zanzibar&amp;#039;&amp;#039;. Именно звездочка, а не имя, предписывает этому домену быть списком.&lt;br /&gt;
&lt;br /&gt;
Элементами в списке может быть что угодно, включая другие списки. Но все элементы в списке должны принадлежать одному домену, и дополнительно к декларации спискового домена должна быть декларация &amp;#039;&amp;#039;&amp;#039;domains&amp;#039;&amp;#039;&amp;#039; для элементов:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;#039;&amp;#039;elements&amp;#039;&amp;#039; должны быть приравнены к простым доменным типам (например, integer, real или symbol) или к набору возможных альтернатив, обозначенных различными функторами. Visual Prolog не допускает смешивание стандартных типов в списке. Например, следующие декларации ошибочно представляют списки, созданные из integers, reals и symbols:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    integer;&lt;br /&gt;
    real;&lt;br /&gt;
    symbol.&lt;br /&gt;
        /* Неправильно */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходом для объявления списков из integer, real и symbols является объявление домена общего для всех типов, где функтор показывает какому типу принадлежит тот или иной элемент. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    i(integer);&lt;br /&gt;
    r(real);&lt;br /&gt;
    s(symbol).&lt;br /&gt;
        /* функторами являются i, r и s */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Подробнее об этом - в этом же руководстве в разделе &amp;quot;Составные списки&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
====Головы и Хвосты====&lt;br /&gt;
&lt;br /&gt;
Список на самом деле является рекурсивным составным объетом. Он состоит из двух частей - головы списка, которым является первый элемент, и хвоста - списка, который включает все следующие элементы.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Хвост списка всегда есть список; голова списка есть элемент.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Например,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;голова списка [a, b, c] есть a&lt;br /&gt;
хвост списка [a, b, c] есть [b, c]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Что происходит, когда мы имеем дело со списком, содержащим один элемент? Ответом является:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;головой списка [c] является c&lt;br /&gt;
хвостом списка [c] является []&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Если многократно отнимать первый элемент от хвоста списка, мы получим в конечном итоге пустой список (&amp;#039;&amp;#039;&amp;#039;[ ]&amp;#039;&amp;#039;&amp;#039;).&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Пустой список не может быть разбит на голову и хвост.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Это означает, что, концептуально говоря, списки имеют древовидную структуру подобно другим составным объектам. Древовидная структура списка [a, b, c, d] есть:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a    list&lt;br /&gt;
      /    \&lt;br /&gt;
     b    list&lt;br /&gt;
         /    \&lt;br /&gt;
        c    list&lt;br /&gt;
            /    \&lt;br /&gt;
           d      []&amp;lt;/pre&amp;gt;&lt;br /&gt;
Более того, одноэлементный список, такой как [a] - это не тот же самый элемент, который этот список содержит, поскольку [a] является действительно составной структурой данных, как это видно здесь:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a     []&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Представления Списков==&lt;br /&gt;
&lt;br /&gt;
Пролог содержит метод для явного обозначения головы и хвоста списка. Вместо разделения элементов запятыми можно отделять голову от хвоста вертикальной чертой (|). Например,&lt;br /&gt;
&lt;br /&gt;
[a, b, c] эквивалентно [a|[b, c]]&lt;br /&gt;
&lt;br /&gt;
и, продолжая процесс,&lt;br /&gt;
&lt;br /&gt;
[a|[b,c]] эквивалентно [a|[b|[c]]],&lt;br /&gt;
&lt;br /&gt;
что эквивалентно [a|[b|[c|[]]]]&lt;br /&gt;
&lt;br /&gt;
Можно даже использовать оба способа разделения в одном и том же списке, рассматривая вертикальную черту как разделитель самого низкого уровня. Следовательно, можно записать [a, b, c, d] как [a, b|[c, d]]. Таблица 1 дает дополнительные примеры.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 1: Головы и Хвосты списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список&lt;br /&gt;
!Голова&lt;br /&gt;
!Хвост&lt;br /&gt;
|-&lt;br /&gt;
|[&amp;#039;a&amp;#039;, &amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[&amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|-&lt;br /&gt;
|[ &amp;#039;a&amp;#039; ]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[] &lt;br /&gt;
|-&lt;br /&gt;
|/*пустой список*/ [ ]&lt;br /&gt;
|неопределен&lt;br /&gt;
|неопределен&lt;br /&gt;
|-&lt;br /&gt;
|[[1, 2, 3], [2, 3, 4], []]&lt;br /&gt;
|[1, 2, 3]&lt;br /&gt;
|[[2, 3, 4], []]&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
В Таблице 2 приведены некоторые примеры унификации списков.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 2: Унификация Списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список 1&lt;br /&gt;
!Список 2&lt;br /&gt;
!Связывание Переменных&lt;br /&gt;
|-&lt;br /&gt;
|[X, Y, Z]&lt;br /&gt;
|[эгберт, ест, мороженое]&lt;br /&gt;
|X=эгберт, Y=ест, Z=мороженое&lt;br /&gt;
|-&lt;br /&gt;
|[7]&lt;br /&gt;
|[X &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Y] &lt;br /&gt;
|X=7, Y=[]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2, 3, 4]&lt;br /&gt;
|[X, Y &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Z]&lt;br /&gt;
|X=1, Y=2, Z=[3,4]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2]&lt;br /&gt;
|[3 &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; X]&lt;br /&gt;
|fail&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Использование Списков==&lt;br /&gt;
&lt;br /&gt;
Поскольку списки являются в действительности рекурсивными составными структурами данных, для их обработки необходимы и рекурсивные алгоритмы. Самый естественный способ обработки списков - сквозной просмотр, в ходе которого что-то делается с каждым элементом, до тех пор, пока не достигнут конец.&lt;br /&gt;
&lt;br /&gt;
Как правило, такого рода алгоритмы используют два клауза. Один из них говорит о том, как поступать с обыкновенным списком, который может быть разделен на голову и хвост. Другой говорит о том, что делать с пустым списком.&lt;br /&gt;
&lt;br /&gt;
===Вывод Списков на печать===&lt;br /&gt;
&lt;br /&gt;
Например, если Вы хотите только вывести на печать элементы списка, то вот что Вы делаете:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  write_a_list : (integer*).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  write_a_list([]). /* Если список пустой, ничего не делаем. */&lt;br /&gt;
  write_a_list([H|T]):- /* Сопоставляем голову с H и хвост с T, и... */&lt;br /&gt;
    stdio::write(H),stdio::nl, /*выводим H и переводим строку*/&lt;br /&gt;
    write_a_list(T).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь мы видим два клауза write_a_list, которые можно выразить но обычном языке:&lt;br /&gt;
&lt;br /&gt;
*Для вывода на печать пустого списка ничего не надо делать.&lt;br /&gt;
*Иначе, для вывода на печать списка, вывести на печать его голову (она есть просто элемент), и потом вывести на печать хвост списка (он, как известно, есть список).&lt;br /&gt;
&lt;br /&gt;
Первый раз, когда вызывается:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такой вызов сопоставляется со вторым клаузом, с головой H=1 и T=[2, 3]. Это приводит к выводу на печать 1, затем рекурсивно вызывается write_a_list с аргументом в виде хвоста списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([2, 3]).&lt;br /&gt;
  /* Это вызов write_a_list(T). */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот второй вызов опять сопоставляется со вторым клаузом, где, на этот раз H=2 и T=[3], поэтому выводится 2 и опять рекурсивно вызывается write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С каким клаузом теперь такой вызов сопоставлятся? Напомним, что, хотя список [3] имеет всего один элемент, у него есть голова и хвост - голова есть 3, а хвост есть []. Таким образом, этот вызов опять сопоставляется со вторым клаузом с H=3 и T=[]. Теперь выводится 3 и  вызывается рекурсивно write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь становится понятно для чего нужен первый клауз. Второй клауз не может быть сопоставлен с таким вызовом, поскольку [] не может быть разделен на голову и хвост. Если бы первого клазуа здесь не было бы, то выполнение goal оказалось бы неуспешным. Но, поскольку он есть, то первый клауз сопоставляется с вызовом и выполнение goal успешно завершается и нечего более не делается.&lt;br /&gt;
&lt;br /&gt;
===Подсчет элементов в Списке===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь, как подсчитать число элементов в списке, или какова длина списка? Логично определить:&lt;br /&gt;
&lt;br /&gt;
*Длина пустого списка [] есть 0.&amp;lt;br /&amp;gt;&lt;br /&gt;
*Длина любого другого списка есть 1 плюс длина его хвоста.&lt;br /&gt;
&lt;br /&gt;
Можно ли это запрограммировать? На Прологе это очень просто. Всего два клауза:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], 0).&lt;br /&gt;
  length_of([_|T], L):-&lt;br /&gt;
    length_of(T, TailLength),&lt;br /&gt;
    L = TailLength + 1.&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L),&lt;br /&gt;
  stdio::write(L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотрите прежде всего на второй клауз. Строго говоря, [_|T] сопоставляется с любым непустым списком, связывая T с хвостом списка. Значение головы неважно, если она есть, она может быть учтена как один элемент.&lt;br /&gt;
&lt;br /&gt;
Тогда вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется со вторым клаузом, с T=[2, 3]. Следующим шагом является вычисление длины хвоста T. Когда это сделано (не имеет значение, как), TailLength получит значение 2, и компьютер теперь может добавить 1 к ней и связать L со значением 3. Как выполняется этот промежуточный шаг? Надо найти длину списка [2, 3], путем удовлетворения цели&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([2, 3], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Другими словами, length_of вызывает себя рекурсивно. Этот вызов сопоставляется со вторым клаузом, связывая&lt;br /&gt;
&lt;br /&gt;
*[3] и T в вызове клаузы и&lt;br /&gt;
*TailLength с L в клаузе.&lt;br /&gt;
&lt;br /&gt;
Подчеркиваем, TailLength в вызове никак не пересекается с TailLength в клаузе, поскольку &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;каждый рекурсивный вызов клауза имеет собственный набор переменных&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Итак, теперь задача - найти длину списка [3], которая есть 1, и мы добавляем 1 к этому значению, чтобы получить длину списка [2, 3], что будет 2. Ну и хорошо!.&lt;br /&gt;
&lt;br /&gt;
Аналогично, length_of вызывает себя рекурсивно опять для получения длины списка [3]. Хвост  [3] есть [], поэтому T связвается с [], и задача теперь - получение длины списка [] и добавление к ней 1, что дает длину списка [3].&lt;br /&gt;
&lt;br /&gt;
Теперь все просто. Цель:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется с &amp;#039;&amp;#039;первым&amp;#039;&amp;#039; клаузом, связывая TailLength с 0. Поэтому теперь компьютер может добавить 1 к нему, получая длину списка [3], и возвращаясь теперь в вызывавший клауз. Это, в свою очередь, опять добавляет 1, давая длину списка [2, 3], и возвращается в клауз, который его вызывал; этот первоначальный клауз добавит снова 1, давая длину списка [1, 2, 3].&lt;br /&gt;
&lt;br /&gt;
Не растерялись? Мы надеемся, нет. В следующей короткой иллюстрации мы сводим воедино все вызовы. Мы использовали здесь прием подстрочника для того, чтобы показать, что аналогично называемые переменные в разных клаузах или различные вызовы того же самого клауза - одно и то же. &lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L1).&lt;br /&gt;
my::length_of([2, 3], L2).&lt;br /&gt;
my::length_of([3], L3).&lt;br /&gt;
my::length_of([], 0).&lt;br /&gt;
L3 =  0+1 = 1.&lt;br /&gt;
L2 = L3+1 = 2.&lt;br /&gt;
L1 = L2+1 = 3.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что Вам не нужно каждый раз создавать такого рода предикаты самостоятельно, Вы можете использовать готовый предикат &amp;#039;&amp;#039;&amp;#039;list::length&amp;#039;&amp;#039;&amp;#039; из PFC.&lt;br /&gt;
&lt;br /&gt;
===Хвостовая рекурсия===&lt;br /&gt;
&lt;br /&gt;
Вы, очевидно, заметили, что length_of не является (и не может быть) предикатом с хвостовой рекурсией, поскольку рекурсивный вызов не является последним шагом в его клаузе. Возможно ли создать предикат, определяющий длину, так, чтобы он был предикатом с хвостовой рекурсией? Да, но это потребует некоторых усилий.&lt;br /&gt;
&lt;br /&gt;
Проблема с предикатом length_of в том, что длину списка нельзя вычислить до тех пор, пока не вычислена длина его хвоста. Но из этой ситуации есть выход. Нам потребуется предикат, вычисляющий длину списка, с тремя аргументами.&lt;br /&gt;
&lt;br /&gt;
*Один из них - это список, от которого компьютер будет откусывать по одному элементу на каждом вызове до тех пор, пока этот список, как и прежде, не превратится в пустой список.&lt;br /&gt;
*Второй - это свободный аргумент, который в конечном итоге вернет результат (длину).&lt;br /&gt;
*Третий - это счетчик, значение которого начинается с нуля и увеличивается с каждым вызовом.&lt;br /&gt;
&lt;br /&gt;
Когда список в конечном итоге станет пустым, мы проунифицируем счетчик с несвязанным результатом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer, integer) procedure(i,o,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], Result, Result).&lt;br /&gt;
  length_of([_|T], Result, Counter):-&lt;br /&gt;
    NewCounter = Counter + 1,&lt;br /&gt;
    length_of(T, Result, NewCounter).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L, 0), /* Начинаем со счетчиком Counter = 0 */&lt;br /&gt;
  stdio::write(&amp;quot; L = &amp;quot;, L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта версия предиката length_of более сложная и во многих смыслах менее логичная, чем предыдущая. Мы ее представили здесь главным образом для того, чтобы показать, что на практике &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;вы можете часто построить алгоритм с хвостовой рекурсией для задач, которые на первый взгляд требуют рекурсии другого типа&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
===Модификация Списка===&lt;br /&gt;
&lt;br /&gt;
Иногда требуется создать другой список из заданного списка. Это делается путем просмотра списка, элемент за элементом, заменяя каждый элемент вычисленным значением. Например, как эта программа, которая добавляет 1 к каждому элементу исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  add1 : (integer*, integer*) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  add1([], [])./* граничное условие */&lt;br /&gt;
  add1([Head|Tail],[Head1|Tail1]):- /* отделяем голову от остального списка*/&lt;br /&gt;
    Head1 = Head+1, /* добавляем 1 к элементу-голове */&lt;br /&gt;
    add1(Tail, Tail1)./* далаем это с остальной частью списка*/&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::add1([1,2,3,4], NewList),&lt;br /&gt;
  stdio::write(NewList)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На обычном языке это звучит так:&lt;br /&gt;
*Добавление 1 ко всем элементам пустого списка порождаем пустой список,&lt;br /&gt;
*Для добавления 1 ко всем элемента любого другого списка:&lt;br /&gt;
**добавить 1 к голове и сделать эту голову головой результирующего списка, а затем&lt;br /&gt;
**добавить 1 к каждому элемента хвоста и этот хвост сделать хвостом результата.&lt;br /&gt;
&lt;br /&gt;
Загрузим программу и выполним такую цель&lt;br /&gt;
&amp;lt;vip&amp;gt;add1([1,2,3,4], NewList).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель вернет&lt;br /&gt;
&amp;lt;vip&amp;gt;NewList=[2,3,4,5]&lt;br /&gt;
1 Solution&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Опять о хвостовой рекурсии===&lt;br /&gt;
&lt;br /&gt;
Является ли предикат add1 проедикатом с хвостовой рекурсией? &lt;br /&gt;
Если у Вас есть опыт использования Lisp или Pascal, Вы могли бы подумать, что нет, поскольку Вы бы рассуждали так:&lt;br /&gt;
&lt;br /&gt;
*Делим список на &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;.&lt;br /&gt;
*Добавляем 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавляя 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
*Соединяем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, что дает результирующий список.&lt;br /&gt;
&lt;br /&gt;
Это не похоже на хвостовую рекурсию, поскольку последний шаг - не рекурсивный вызов.&lt;br /&gt;
&lt;br /&gt;
Однако, и это важно, – &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Это не то, что делает Пролог&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;. В Visual Prolog add1 является предикатом с хвостовой рекурсией, поскольку выполняется в действительности следующим образом:&lt;br /&gt;
&lt;br /&gt;
*Связать голову и хвост исходного списка с &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, соответственно.&lt;br /&gt;
*Связать голову и хвост результирующего списка с &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, соответственно. (&amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; пока не получили значений.)&lt;br /&gt;
*Добавить 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавить 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Когда это сделано, &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;уже являются&amp;#039;&amp;#039;&amp;#039; головой и списком результата и отдельной операции по их соединению нет. Поэтому рекурсивный вызов и является последним шагом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h6&amp;gt;Снова Модификация Списков&amp;lt;/h6&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Конечно, не всегда модификации подлежит каждый элемент. Посмотрим на программу, которая сканирует список чисел и копирует его, удаляя отрицательные числа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  discard_negatives : (integer*, integer*) procedure(i,o). /*удалить отрицательные*/&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  discard_negatives([], []).&lt;br /&gt;
  discard_negatives([H|T], ProcessedTail):-&lt;br /&gt;
    H &amp;lt; 0,&lt;br /&gt;
    !, /* Если H отрицательно, пропускаем его */&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
  discard_negatives([H|T], [H|ProcessedTail]):-&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::discard_negatives ([2, -45, 3, 468], X),&lt;br /&gt;
  stdio::write(X).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напрмер, цель&lt;br /&gt;
&amp;lt;vip&amp;gt;my::discard_negatives([2, -45, 3, 468], X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
дает&lt;br /&gt;
&amp;lt;vip&amp;gt;X=[2, 3, 468].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
А вот - предикат который копирует элементы списка, добавляя для каждого элемента его дубликат:&lt;br /&gt;
&amp;lt;vip&amp;gt;doubletalk([], []).&lt;br /&gt;
doubletalk([H|T], [H, H|DoubledTail]) :-&lt;br /&gt;
  doubletalk(T, DoubledTail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Принадлежнось списку===&lt;br /&gt;
&lt;br /&gt;
Допустим, имеется список с именами &amp;#039;&amp;#039;John&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Leonard&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Eric&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Frank&amp;#039;&amp;#039; и требуется, используя Visual Prolog, выяснить, принадлежит ли заданное имя этому списку. Другими словами, надо определить &amp;quot;отношение&amp;quot; между двумя аргументами: именем и списком имен. Это соответствует предикату&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember : (name, name*).&lt;br /&gt;
  /* &amp;quot;name&amp;quot; принадлежит списку &amp;quot;name*&amp;quot; */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В программе e01.pro первый клауз исследует голову списка. Если голова списка совпадает с искомым именем, то можно сделать заключение, что Name принадлежит списку. Поскольку хвост списка нас не интересует, то это мы представляем анонимной переменной. Благодаря первому клаузу, цель&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;])&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
удовлетворена.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Программа e01.pro */&lt;br /&gt;
class my&lt;br /&gt;
predicates&lt;br /&gt;
  isMember : (A, A*) determ.&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  isMember(Name, [Name|_]) :-&lt;br /&gt;
    !.&lt;br /&gt;
  isMember(Name, [_|Tail]):-&lt;br /&gt;
    isMember(Name,Tail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;]),&lt;br /&gt;
  !,&lt;br /&gt;
  stdio::write(&amp;quot;Success&amp;quot;)&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если голова списка не есть Name, то надо исследовать, не содержится ли Name в хвосте списка.&lt;br /&gt;
&lt;br /&gt;
На обычном языке:&lt;br /&gt;
&lt;br /&gt;
Name принадлежит списку, если Name является первым элементом списка, или&amp;lt;br /&amp;gt;&lt;br /&gt;
Name принадлежит списку, если Name принадлежит хвосту.&lt;br /&gt;
&lt;br /&gt;
Второй клауз предиката isMember относится к этому отношению. Таким образом на Visual Prolog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember(Name, [_|Tail]) :-&lt;br /&gt;
    isMember(Name, Tail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Добавление списка к другому списку: декларативное и рекурсивное решения===&lt;br /&gt;
&lt;br /&gt;
Рассмотренный предикат member программы e01.pro работает в двух направлениях. Вернемся к его клаузам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
member(Name, [Name|_]).&lt;br /&gt;
member(Name, [_|Tail]) :-&lt;br /&gt;
  member(Name, Tail).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На эти клаузы можно смотреть с двух различных точек зрения: декларативной и процедурной.&lt;br /&gt;
&lt;br /&gt;
*С декларативной точки зрения, клаузы выражают: &lt;br /&gt;
**Name (Имя) принадлежит списку, если голова списка есть Name&lt;br /&gt;
**иначе Name принадлежит списку, если оно (Имя) принадлежит хвосту.&lt;br /&gt;
*С процедурной точки зрения, эти же два клауза могут быть интерпретированы так:&amp;lt;br/&amp;gt;Чтобы найти элемент списка &lt;br /&gt;
**Найдите его голову&lt;br /&gt;
**иначе найдите элемент хвоста списка.&lt;br /&gt;
&lt;br /&gt;
Эти две точки зрения соответствуют целям&lt;br /&gt;
&amp;lt;vip&amp;gt;member(2, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;member(X, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате первый вызов поручает Visual Prolog(у) проверить, истино ли нечто (принадлежность числа 2 списку [1,2,3,4]). Второй вызов поручает Visual Prolog(у) найти все члены списка [1,2,3,4]. Не смущайтесь этим. Предикат member является одним и тем же, но на его поведение можно смотреть под разными углами.&lt;br /&gt;
&lt;br /&gt;
====Рекурсия с процедуральной точки зрения====&lt;br /&gt;
&lt;br /&gt;
Прелесть Пролога заключается в том, что часто, когда мы конструируем клаузы для предиката, будучи на одной точке зрения, они будут работать и при взгляде с другой точки зрения. Чтобы обнаружить эту дуальность, мы приведем пример предиката для добавления (append) одного списка к другому. Мы определяем предикат append с тремя аргументами:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append(List1, List2, List3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот предикат интегрирует списки List1 и List2 в форму списка List3 так, что список List2 дописывается в конце списка List1. То есть содержательно - осуществляется добавление списка List2 к списку LIst1. Опять мы используем рекурсию (на этот раз с процедуральной точки зрения).&lt;br /&gt;
&lt;br /&gt;
Если список List1 пустой, результатом добавления списка List1 к списку List2 будет тот же самый List2. Запишем это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([], List2, List2).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если список List1 не пустой, то можно преобразовать списки List1 и List2 к форме списка &amp;#039;&amp;#039;List3&amp;#039;&amp;#039;, сделав голову списка List1 головой списка List3. В приведенном коде переменная H используется в качестве головы как списка List1, так и списка List3. хвост списка List3 есть список L3, который составлен из остатка списка List1 (а именно, L1) и всего списка List2. Опять выразим это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([H|L1], List2, [H|L3]) :-&lt;br /&gt;
   append(L1, List2, L3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append работает следующим образом: пока список List1 не пустой, рекурсивное правило дописывает один элемент каждый раз к списку List3. Когда список List1 становится пустым, первый клауз  clause обеспечивает дописывание списка List2 в конец списка List3.&lt;br /&gt;
&lt;br /&gt;
====Варианты использования одного предиката====&lt;br /&gt;
&lt;br /&gt;
Подходя к предикату append с декларативной точки зрения, мы определили его как отношение между тремя списками. Это отношение справедливо также, если списки List1 и List3 известны, а List2 - нет. Более того, это также работает, если только List3 известен. Например, для того, чтобы выяснить, какие два списка могли бы быть соединены для получения известного списка, можно использовать вызов в форме&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, L2, [1, 2, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С таким целевым вызовом, Visual Prolog найдет следующие решения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
L1=[], L2=[1,2,4]&lt;br /&gt;
L1=[1], L2=[2,4]&lt;br /&gt;
L1=[1,2], L2=[4]&lt;br /&gt;
L1=[1,2,4], L2=[]&lt;br /&gt;
4 Solutions&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно использовать предикат append для нахождения списка, который следовало бы добавить к списку [3,4] для получения списка[1,2,3,4]. Попробуем такой вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, [3,4], [1,2,3,4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Visual Prolog находит решение&lt;br /&gt;
&amp;lt;vip&amp;gt;L1=[1,2].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append определяет отношение между &amp;#039;&amp;#039;входным набором (input set)&amp;#039;&amp;#039; и &amp;#039;&amp;#039;выходным набором (output set)&amp;#039;&amp;#039; таким образом, что отношение применимо в обе стороны. При таком отношении возникает вопрос&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Что является выходным набором для заданного входного?&amp;#039;&amp;#039; или &amp;#039;&amp;#039;Какой входной набор соответствует заданному выходному?&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Статус аргументов данного вызова предиката известен как &amp;#039;&amp;#039;поток (или шаблон) ввода-вывода&amp;#039;&amp;#039;. Аргумент, который связан или наследуется в момент вызова является входным аргументом и обозначается как (i). Свободный аргумент является выходным аргументом и обозначается как (o).&lt;br /&gt;
&lt;br /&gt;
Предикат append обладает свойством поддерживать любой шаблон ввода-вывода, какой требуется. Однако не все предикаты имеют возможность вызова с различными шаблонами ввода-вывода. Когда клауз Пролога способен поддерживать множество шаблонов ввода-вывода, он называется инверсным клаузом. Целый набор предикатов для обработки списков содержится в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Все Решения Сразу==&lt;br /&gt;
&lt;br /&gt;
Откаты и рекурсии являются двумя способами выполнения повторяющихся процессов. Рекурсия предпочтительнее, поскольку, в отличие от отката, позволяет передать данные через аргументы от одного рекурсивного вызова к следующему. Благодяря этому, рекурсивные процедуры могут использовать промежуточные результаты или счетчики по ходу выполнения.&lt;br /&gt;
&lt;br /&gt;
Однако есть одна вещь, которую, в отличие от рекурсии, могут делать откаты, а именно – искать все альтернитивные решения посредством одного вызова. Поэтому Вы можете оказаться в затрудинтельном положении: Вам нужно получить все решения за один вызов, и, при этом, они Вам нужны все сразу, как часть единой интегрированной структуры данных. Что делать?&lt;br /&gt;
&lt;br /&gt;
К счастью, Visual Prolog позволяет найти выход из этого положения. Предопределенная конструкция обработки списков получает целевой вызов в качестве своего аргумента и собирает все решиния для этого вызова в единый выходной список. Такаая конструкция для списков имеет два аргумента:&lt;br /&gt;
&lt;br /&gt;
*Первый аргумент, VarName, определяет аргумент в вызываемом целевом предикате, значения которого будут собираться в список.&lt;br /&gt;
*Второй - mypredicate - определяет предикат, который будет получать значения.&lt;br /&gt;
*Выходной параметр ListParam, является переменной, которая содержит список значений, полученных в ходе отката.&lt;br /&gt;
&lt;br /&gt;
Программа e02.pro использует обработку списка для вывода среднего возраста группы людей.&lt;br /&gt;
&amp;lt;vip&amp;gt;        /* Программа e02.pro */&lt;br /&gt;
class my&lt;br /&gt;
domains&lt;br /&gt;
  name = string.&lt;br /&gt;
  address = string.&lt;br /&gt;
  age = integer.&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  person : (name, address, age) nondeterm anyflow.&lt;br /&gt;
  sumlist : (age*, age, integer)procedure(i,o,o).&lt;br /&gt;
end class my&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  sumlist([],0,0).&lt;br /&gt;
  sumlist([H|T], Sum, N):-&lt;br /&gt;
    sumlist(T, S1, N1),&lt;br /&gt;
    Sum=H+S1, N=1+N1.&lt;br /&gt;
  &lt;br /&gt;
  person(&amp;quot;Sherlock Holmes&amp;quot;,&amp;quot;22B Baker Street&amp;quot;, 42).&lt;br /&gt;
  person(&amp;quot;Pete Spiers&amp;quot;,&amp;quot;Apt. 22, 21st Street&amp;quot;, 36).&lt;br /&gt;
  person(&amp;quot;Mary Darrow&amp;quot;,&amp;quot;Suite 2, Omega Home&amp;quot;, 51).&lt;br /&gt;
end implement my&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  L = [ Age || my::person(_, _, Age)],&lt;br /&gt;
  my::sumlist(L, Sum, N),&lt;br /&gt;
  Ave = Sum/N,&lt;br /&gt;
  stdio::write(&amp;quot;Average=&amp;quot;, Ave, &amp;quot;. &amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Клауз обработки списка в этой программе создает список L, который является списком всех возрастов, полученных от предиката person. Еслы бы потребовалось бы собрать список всех людей в возрасте 42 лет, можно было бы задать следующий цель-вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ Who || my::person(Who, _, 42) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий код порождает список всех положительных чисел исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ X || X = list::getMember_nd([2,-8,-3,6]), X &amp;gt; 0]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Смешанные списки==&lt;br /&gt;
&lt;br /&gt;
Список целых объявляется просто&lt;br /&gt;
&amp;lt;vip&amp;gt;integer*&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же верно и для списков вещественных чисел (integer), символьных (symbol) списков или списков строк (string).&lt;br /&gt;
&lt;br /&gt;
Однако часто приходится хранить комбинацию различных типов элементов в составе одного и того же списка, такую как:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[2, 3, 5.12, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot;].&lt;br /&gt;
        /* Не корректно для Visual Prolog*/&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Смешанные списки&amp;#039;&amp;#039; - списки, содержащие элементы более чем одного типа. Для работы со списками разнотипных элементов должны использоваться специальные объявления, поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;одному и тому же&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; домену. Способом создания списка, который хранит такие различные типы элементов является использование функторов, поскольку домен может представляться &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;более, чем одним&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; функтором, каждый со своими т&amp;#039;&amp;#039;&amp;#039;и&amp;#039;&amp;#039;&amp;#039;повыми аргументами.&lt;br /&gt;
&lt;br /&gt;
Пример объаявления списка, который может хранить целые, символы, строки или списки таких  даных:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  /* функторами являются l, i, c, and s */&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Список&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 2, 9, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot; ]&lt;br /&gt;
/* Не корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
на Visual Prolog следовало бы записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[i(2), i(9), l([s(&amp;quot;food&amp;quot;), s(&amp;quot;goo&amp;quot;)]), s(&amp;quot;new&amp;quot;)]&lt;br /&gt;
        /* Корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример предиката append показывает как использовать такого рода декларации в стандартных программах манипулирования списками.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  append : (A*,A*,A*) procedure (i,i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  append([], L, L).&lt;br /&gt;
  append([X|L1], L2, [X|L3]):-&lt;br /&gt;
    append(L1, L2, L3).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::s(&amp;quot;likes&amp;quot;),my::l([my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;mary&amp;quot;)])],&lt;br /&gt;
    [my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;sue&amp;quot;)], &lt;br /&gt;
    Ans&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Первый список: &amp;quot;, Ans,&amp;quot;\n\n&amp;quot;),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::l([my::s(&amp;quot;This&amp;quot;),my::s(&amp;quot;is&amp;quot;),my::s(&amp;quot;a&amp;quot;),my::s(&amp;quot;list&amp;quot;)]),my::s(&amp;quot;bee&amp;quot;)], &lt;br /&gt;
    [my::c(&amp;#039;c&amp;#039;)], &lt;br /&gt;
    Ans2&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Второй списко: &amp;quot;, Ans2, &amp;quot;\n\n&amp;amp;quot).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Разбор с использованием списков==&lt;br /&gt;
&lt;br /&gt;
Ниже приведена программа, демонстрирующая [[wikipedia:parsing|разбор]] (parsing)с использованием списков. Процесс разбора работает в методом сворачивания. В этом примере входная строка преобразуется в структуру данных Пролога, которая может быть использована далее.&lt;br /&gt;
&lt;br /&gt;
Этот разборщик предназначен для примитивного формального языка. Хотя этот пример достаточно сложен с точки зрения этого руководства, мы решили поместит его здесь, поскольку разбор является одной из областей, где Visual Prolog очень эффективен. Если Вы чувствуете себя не вполне готовым для этого раздела, Вы можете этот пример пропустить и продолжить чтение руководства без потери содержательности.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;#include @&amp;quot;pfc\exception\exception.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\string\string.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\console\console.ph&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class my_t&lt;br /&gt;
predicates&lt;br /&gt;
  tokl : (string, string*) procedure (i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_t&lt;br /&gt;
clauses&lt;br /&gt;
  tokl(Str, [H|T]) :-&lt;br /&gt;
    string::fronttoken(Str, H, Str1),&lt;br /&gt;
    !,&lt;br /&gt;
    tokl(Str1, T).&lt;br /&gt;
  tokl(_, []).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
/* * * * * * * * * * * * * * * * * * * *  *&lt;br /&gt;
* Вторая часть этой программы - разборщик *&lt;br /&gt;
* * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
class my_p&lt;br /&gt;
domains&lt;br /&gt;
  program = program(statement*).&lt;br /&gt;
  /* * * * * * * * * * * * * * * * * * * * *  * &lt;br /&gt;
  * Определение что есть языковая конструкция *&lt;br /&gt;
  * (предложение языка)                       *&lt;br /&gt;
  * * * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
  statement =&lt;br /&gt;
    if_Then_Else(exp, statement, statement);&lt;br /&gt;
    if_Then(exp, statement);&lt;br /&gt;
    while(exp, statement);&lt;br /&gt;
    assign(id, exp).&lt;br /&gt;
  /* * * * * * * * * * *  *&lt;br /&gt;
  * Определение выражения *&lt;br /&gt;
  * * * * * * * *  * * *  */&lt;br /&gt;
  exp = &lt;br /&gt;
    plus(exp, exp);&lt;br /&gt;
    minus(exp, exp);&lt;br /&gt;
    var(id);&lt;br /&gt;
    int(integer).&lt;br /&gt;
    id = string.&lt;br /&gt;
&lt;br /&gt;
  predicates&lt;br /&gt;
    s_program : (string*, program) procedure (i,o).&lt;br /&gt;
    s_statement : (string*, string*, statement) determ (i,o,o).&lt;br /&gt;
    s_statement_list : (string*, string*, statement*) determ (i,o,o).&lt;br /&gt;
    s_exp : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
    s_exp1 : (string*, string*, exp, exp) determ (i,o,i,o).&lt;br /&gt;
    s_exp2 : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_p&lt;br /&gt;
clauses&lt;br /&gt;
  s_program(TokenList, program(StatementList)):-&lt;br /&gt;
    s_statement_list(TokenList, _, StatementList),&lt;br /&gt;
    !.&lt;br /&gt;
  s_program(_, program([])).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement_list([], [], []) :- !.&lt;br /&gt;
  s_statement_list(List1, List4, [Statement|Program]) :-&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;;&amp;quot;|List3],&lt;br /&gt;
    s_statement_list(List3, List4, Program).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List7,if_then_else(Exp,Statement1, Statement2)):-&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement1),&lt;br /&gt;
    List4=[&amp;quot;else&amp;quot;|List5],!,&lt;br /&gt;
    s_statement(List5, List6, Statement2),&lt;br /&gt;
    List6=[&amp;quot;fi&amp;quot;|List7].&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List5,if_then(Exp, Statement)) :- !,&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement),&lt;br /&gt;
    List4=[&amp;quot;fi&amp;quot;|List5].&lt;br /&gt;
  s_statement([&amp;quot;do&amp;quot;|List1], List4,while(Exp, Statement)) :- !,&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;while&amp;quot;|List3],&lt;br /&gt;
    s_exp(List3, List4, Exp).&lt;br /&gt;
  s_statement([ID|List1], List3,assign(Id,Exp)) :-&lt;br /&gt;
    string::isname(ID),&lt;br /&gt;
    List1=[&amp;quot;=&amp;quot;|List2],&lt;br /&gt;
    s_exp(List2, List3, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp(List1, List3, Exp):-&lt;br /&gt;
    s_exp2(List1, List2, Exp1),&lt;br /&gt;
    s_exp1(List2, List3, Exp1, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp1([&amp;quot;+&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, plus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1([&amp;quot;-&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, minus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1(List, List, Exp, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp2([Int|Rest], Rest, int(I)) :-&lt;br /&gt;
    trap(I = toTerm(Int),Error,exception::clear_fail(Error)),&lt;br /&gt;
    !.&lt;br /&gt;
  s_exp2([Id|Rest], Rest, var(Id)) :-&lt;br /&gt;
    string::isname(Id).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  stdio::write(Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res),&lt;br /&gt;
  stdio::write(Res).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Загрузите программу и выполните целевой вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;goal&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Prolog вернет структуру программы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;Ans = [&amp;quot;b&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;if&amp;quot;,&amp;quot;b&amp;quot;,&amp;quot;then&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;1&amp;quot;,&lt;br /&gt;
    &amp;quot;else&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;fi&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;do&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;a&amp;quot;,&lt;br /&gt;
    &amp;quot;-&amp;quot;,&amp;quot;1&amp;quot;,&amp;quot;while&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;;&amp;quot;],&lt;br /&gt;
Res=program&lt;br /&gt;
  (&lt;br /&gt;
  [assign(&amp;quot;b&amp;quot;,int(2)),&lt;br /&gt;
   if_then_else&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;b&amp;quot;),&lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(1)), &lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(2))&lt;br /&gt;
     ),&lt;br /&gt;
   while&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;a&amp;quot;),&lt;br /&gt;
     assign&lt;br /&gt;
       (&lt;br /&gt;
       &amp;quot;a&amp;quot;,&lt;br /&gt;
       minus&lt;br /&gt;
         (&lt;br /&gt;
         var(&amp;quot;a&amp;quot;),&lt;br /&gt;
         int(1)&lt;br /&gt;
         )&lt;br /&gt;
       )&lt;br /&gt;
     )&lt;br /&gt;
  ])&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Преобразование в этом примере представлено двумя шагами: сканирование и разбор. Предикат tokl  - это сканер. Он принимает строку и преобразует ее в список токенов. Все предикаты с именами, начинающимися с s_ являются предикатами парсера. В этом примере входный текст представляет программу на языке, подобном языку Паскаль, и состоящей из Паскале-подобных предложений. Этот язык программирования позволяет только предложения вида: IF THEN ELSE, IF THEN, DO WHILE и присваивание (ASSIGNMENT). Предложения состоят из выражений и вложенных предложений. Выражения - сложение, вычитание, переменные и целые.&lt;br /&gt;
&lt;br /&gt;
Далее, как это работает:&lt;br /&gt;
&lt;br /&gt;
*Первый клауз парсера, s_program, принимает список токенов и пытается преобразовать его в список предложений.&lt;br /&gt;
*Предикат s_statement_list принимает тот же самый список токенов и проверяет возможность деления токенов на предложения, завершающиеся точкой с запятой.&lt;br /&gt;
*Предикат s_statement проверяет могут ли начальные токены списка (токенов) представлять собой правильное предложение. Если да, то такое предложение возвращается в виде структуры, а остаток токенов передается рекурсивно вызываемому предикату s_statement_list.&lt;br /&gt;
*Четыре клауза предиката s_statement соответствуют четырем типам, которые парсер понимает.&amp;lt;br\&amp;gt; Если первый клауз предиката s_statement не может преобразовать список токенов в предложение вида IF THEN ELSE, то клауз завершается неуспешно и в порядке отката переходит к следующему клаузу предиката s_statement. Теперь делается попытка преобразовать список токенов в конструкцию вида IF THEN. Если эта попытка неуспешна, то в следующем клаузе делается попытка преобразования к предложению вида DO WHILE.&lt;br /&gt;
*Если первые три клауза предиката s_statement завершаются неуспешно, то последний клауз этого предиката провереяет представлена ли в списке токенов операция присваивания. Этот клауз проверяет &amp;quot;на присваивание&amp;quot; является ли первый терм символом, второй - знаком равно (&amp;quot;=&amp;quot;), а следующие термы представляют простое математическое выражение.&lt;br /&gt;
*Предикаты s_exp, s_exp1 и s_exp2 работают аналогично, путем проверки являются ли начальные термы выражениями и, если это так, то предикату s_statement предикат s_exp возвращает остаток термов и математическое выражение в виде структуры.&lt;br /&gt;
&lt;br /&gt;
==Заключение==&lt;br /&gt;
&lt;br /&gt;
В этом руководстве раскрыта следующие важные моменты:&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;Списки&amp;#039;&amp;#039; могут содержать произвольное число элементов; Вы объявляете их простым добавление звездочки (&amp;#039;&amp;#039;&amp;#039;*&amp;#039;&amp;#039;&amp;#039;) в конце ранее определенного домена.&lt;br /&gt;
*Список является рекурсивным составным объектом, состоящим из головы и хвоста. Голова есть первый элемент, а хвост - остальная часть списка (без первого элемента). Хвост списка - всегда список; голова списка - всегда элемента. Список может содержать ноль или более элементов; Пустой список обозначается [].&lt;br /&gt;
*Элементами списка можже быть что угодно, включая другиме списки; все элементы списка должны принадлежать одному домену. В этом случае объявление домена элементов должно выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где elements = один из стандартных доменов (integer, real, etc.) или набор альтернатив  обозначенных различными функторами (int(integer); rl(real); smb(symbol); и т.д.). Смешение типов в списках языка системы Visual Prolog допускается только включением их в составные объекты или функторы.&lt;br /&gt;
*Можно использовать разделители (запятые, [ и |) для явного отделения головы списка от хвоста. Так, список&lt;br /&gt;
&amp;lt;vip&amp;gt;[a, b, c, d]&amp;lt;/vip&amp;gt;&lt;br /&gt;
может быть записан как:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
[a|[b, c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b|[c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b, c|[d]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c, d]]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c|[d]]]] &lt;br /&gt;
или даже&lt;br /&gt;
[a|[b|[c|[d|[]]]]]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Обработка списков заключается в рекурсивном отщеплении головы списка (и выполнении действий над ней) до опустошения списка.&lt;br /&gt;
*Предикаты для работы со списками содержаться в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
*Visual Prolog поддерживает встроенную конструкцию обработки списков, которая принимает целевой предикат в качестве одного из аргументов и собирает все решения этого целевого предиката в едином списке. Синтаксис такой конструкции&lt;br /&gt;
&amp;lt;vip&amp;gt;Result = [ Argument || myPredicate(Argument) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы одному и тому же домену, следует использовать функторы для создания списков, хранящих элементы различного типа.&lt;br /&gt;
*процесс &amp;#039;&amp;#039;разбора с использованием разностных списков (parsing by difference lists)&amp;#039;&amp;#039; работает путем сокращения задачи; пример в этом руководстве преобрахует строку входных данных в структуру, которая может обрабатываться позже.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[[en:Lists and Recursion]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1589</id>
		<title>Списки и рекурсия</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8_%D0%B8_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&amp;diff=1589"/>
		<updated>2007-11-23T10:56:09Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: очепятки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Обработка списков как обработка последовательности элементов - это мощная техника, используемая в Прологе. В этом руководстве мы рассматриваем что такое списки, как их объявлять, и затем приводим несколько примеров, показывающих как использовать работу со списками в приложениях. Мы также определяем два хорошо известных предиката Пролога – member (член, элемент) и append (добавить) – рассматривая обработку списков как с рекурсивной, так и процедурной точки зрения.&lt;br /&gt;
&lt;br /&gt;
Затем мы представляем предикат findall - стандартный предикат языка системы Visual Prolog, позволяющий собирать решения в единую цель. В завершение этого руководства мы обсуждаем составные списки – комбинции различных типов элементов и, кроме того, - пример разбора с помощью разностных списков.&lt;br /&gt;
&lt;br /&gt;
==Что такое Список?==&lt;br /&gt;
&lt;br /&gt;
В Прологе &amp;#039;&amp;#039;список (list)&amp;#039;&amp;#039; является объектом, содержащим внутри произвольное число других объектов. Списки соответствуют, грубо говоря, массивам в других языках, но, в отличие от массивов, список не трубует декларирования его размера до начала его использования.&lt;br /&gt;
&lt;br /&gt;
Список, содержащий числа 1, 2 и 3 записывается как &lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 3 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порядок элементов в этом списке значим:&lt;br /&gt;
*Число &amp;quot;1&amp;quot; является первым элементом,&lt;br /&gt;
*&amp;quot;2&amp;quot; - второй,&lt;br /&gt;
*&amp;quot;3&amp;quot; - третий.&lt;br /&gt;
&lt;br /&gt;
Список [ 1, 2, 3 ] и список [ 1, 3, 2 ] различны.&lt;br /&gt;
&lt;br /&gt;
Каждый компонент списка называется элемент (element). Для того, чтобы сформировать списковую структуру данных, следует разделять элементы запятыми и заключать их всех в квадратные скобки. Посмотрим на некоторые примеры:&lt;br /&gt;
&amp;lt;vip&amp;gt;[&amp;quot;dog&amp;quot;, &amp;quot;cat&amp;quot;, &amp;quot;canary&amp;quot;]&lt;br /&gt;
[&amp;quot;valerie ann&amp;quot;, &amp;quot;jennifer caitlin&amp;quot;, &amp;quot;benjamin thomas&amp;quot;]&amp;lt;/vip&amp;gt;&lt;br /&gt;
Один и тот же элемент может быть представлен в списке несколько раз, например:&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 1, 2, 1, 3, 1 ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Объявление Списков===&lt;br /&gt;
Для объявления домена - списка целых используется декларация домена, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Звездочка означает &amp;quot;список этого&amp;quot;; то есть, integer* означает &amp;quot;список целых&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на то, что слово &amp;#039;&amp;#039;&amp;quot;list&amp;quot;&amp;#039;&amp;#039; не имеет специального значения в Visual Prolog. Вы равным образом могли бы назвать Ваш списковый домен как &amp;#039;&amp;#039;zanzibar&amp;#039;&amp;#039;. Именно звездочка, а не имя, предписывает этому домену быть списком.&lt;br /&gt;
&lt;br /&gt;
Элементами в списке может быть что угодно, включая другие списки. Но все элементы в списке должны принадлежать одному домену, и дополнительно к декларации спискового домена должна быть декларация &amp;#039;&amp;#039;&amp;#039;domains&amp;#039;&amp;#039;&amp;#039; для элементов:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;#039;&amp;#039;elements&amp;#039;&amp;#039; должны быть приравнены к простым доменным типам (например, integer, real или symbol) или к набору возможных альтернатив, обозначенных различными функторами. Visual Prolog не допускает смешивание стандартных типов в списке. Например, следующие декларации ошибочно представляют списки, созданные из integers, reals и symbols:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    integer;&lt;br /&gt;
    real;&lt;br /&gt;
    symbol.&lt;br /&gt;
        /* Неправильно */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходом для объявления списков из integer, real и symbols является объявление домена общего для всех типов, где функтор показывает какому типу принадлежит тот или иной элемент. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;element_list = elements*.&lt;br /&gt;
elements =&lt;br /&gt;
    i(integer);&lt;br /&gt;
    r(real);&lt;br /&gt;
    s(symbol).&lt;br /&gt;
        /* функторами являются i, r и s */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Подробнее об этом - в этом же руководстве в разделе &amp;quot;Составные списки&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
====Головы и Хвосты====&lt;br /&gt;
&lt;br /&gt;
Список на самом деле является рекурсивным составным объетом. Он состоит из двух частей - головы списка, которым является первый элемент, и хвоста - списка, который включает все следующие элементы.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Хвост списка всегда есть список; голова списка есть элемент.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Например,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;голова списка [a, b, c] есть a&lt;br /&gt;
хвост списка [a, b, c] есть [b, c]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Что происходит, когда мы имеем дело со списком, содержащим один элемент? Ответом является:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;головой списка [c] является c&lt;br /&gt;
хвостом списка [c] является []&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Если многократно отнимать первый элемент от хвоста списка, мы получим в конечном итоге пустой список (&amp;#039;&amp;#039;&amp;#039;[ ]&amp;#039;&amp;#039;&amp;#039;).&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Пустой список не может быть разбит на голову и хвост.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Это означает, что, концептуально говоря, списки имеют древовидную структуру подобно другим составным объектам. Древовидная структура списка [a, b, c, d] есть:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a    list&lt;br /&gt;
      /    \&lt;br /&gt;
     b    list&lt;br /&gt;
         /    \&lt;br /&gt;
        c    list&lt;br /&gt;
            /    \&lt;br /&gt;
           d      []&amp;lt;/pre&amp;gt;&lt;br /&gt;
Более того, одноэлементный список, такой как [a] - это не тот же самый элемент, который этот список содержит, поскольку [a] является действительно составной структурой данных, как это видно здесь:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    list&lt;br /&gt;
   /    \&lt;br /&gt;
  a     []&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Представления Списков==&lt;br /&gt;
&lt;br /&gt;
Пролог содержит метод для явного обозначения головы и хвоста списка. Вместо разделения элементов запятыми можно отделять голову от хвоста вертикальной чертой (|). Например,&lt;br /&gt;
&lt;br /&gt;
[a, b, c] эквивалентно [a|[b, c]]&lt;br /&gt;
&lt;br /&gt;
и, продолжая процесс,&lt;br /&gt;
&lt;br /&gt;
[a|[b,c]] эквивалентно [a|[b|[c]]],&lt;br /&gt;
&lt;br /&gt;
что эквивалентно [a|[b|[c|[]]]]&lt;br /&gt;
&lt;br /&gt;
Можно даже использовать оба способа разделения в одном и том же списке, рассматривая вертикальную черту как разделитель самого низкого уровня. Следовательно, можно записать [a, b, c, d] как [a, b|[c, d]]. Таблица 1 дает дополнительные примеры.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 1: Головы и Хвосты списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список&lt;br /&gt;
!Голова&lt;br /&gt;
!Хвост&lt;br /&gt;
|-&lt;br /&gt;
|[&amp;#039;a&amp;#039;, &amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[&amp;#039;b&amp;#039;, &amp;#039;c&amp;#039;]&lt;br /&gt;
|-&lt;br /&gt;
|[ &amp;#039;a&amp;#039; ]&lt;br /&gt;
|&amp;#039;a&amp;#039;&lt;br /&gt;
|[] &lt;br /&gt;
|-&lt;br /&gt;
|/*пустой список*/ [ ]&lt;br /&gt;
|неопределен&lt;br /&gt;
|неопределен&lt;br /&gt;
|-&lt;br /&gt;
|[[1, 2, 3], [2, 3, 4], []]&lt;br /&gt;
|[1, 2, 3]&lt;br /&gt;
|[[2, 3, 4], []]&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
В Таблице 2 приведены некоторые примеры унификации списков.&lt;br /&gt;
&lt;br /&gt;
{|cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+&amp;#039;&amp;#039;&amp;#039;Таблица 2: Унификация Списков&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
!Список 1&lt;br /&gt;
!Список 2&lt;br /&gt;
!Связывание Переменных&lt;br /&gt;
|-&lt;br /&gt;
|[X, Y, Z]&lt;br /&gt;
|[эгберт, ест, мороженое]&lt;br /&gt;
|X=эгберт, Y=ест, Z=мороженое&lt;br /&gt;
|-&lt;br /&gt;
|[7]&lt;br /&gt;
|[X &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Y] &lt;br /&gt;
|X=7, Y=[]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2, 3, 4]&lt;br /&gt;
|[X, Y &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; Z]&lt;br /&gt;
|X=1, Y=2, Z=[3,4]&lt;br /&gt;
|-&lt;br /&gt;
|[1, 2]&lt;br /&gt;
|[3 &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; X]&lt;br /&gt;
|fail&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Использование Списков==&lt;br /&gt;
&lt;br /&gt;
Поскольку списки являются в действительности рекурсивными составными структурами данных, для их обработки необходимы и рекурсивные алгоритмы. Самый естественный способ обработки списков - сквозной просмотр, в ходе которого что-то делается с каждым элементом, до тех пор, пока не достигнут конец.&lt;br /&gt;
&lt;br /&gt;
Как правило, такого рода алгоритмы используют два клауза. Один из них говорит о том, как поступать с обыкновенным списком, который может быть разделен на голову и хвост. Другой говорит о том, что делать с пустым списком.&lt;br /&gt;
&lt;br /&gt;
===Вывод Списков на печать===&lt;br /&gt;
&lt;br /&gt;
Например, если Вы хотите только вывести на печать элементы списка, то вот что Вы делаете:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  write_a_list : (integer*).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  write_a_list([]). /* Если список пустой, ничего не делаем. */&lt;br /&gt;
  write_a_list([H|T]):- /* Сопоставляем голову с H и хвост с T, и... */&lt;br /&gt;
    stdio::write(H),stdio::nl, /*выводим H и переводим строку*/&lt;br /&gt;
    write_a_list(T).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь мы видим два клауза write_a_list, которые можно выразить но обычном языке:&lt;br /&gt;
&lt;br /&gt;
*Для вывода на печать пустого списка ничего не надо делать.&lt;br /&gt;
*Иначе, для вывода на печать списка, вывести на печать его голову (она есть просто элемент), и потом вывести на печать хвост списка (он, как известно, есть список).&lt;br /&gt;
&lt;br /&gt;
Первый раз, когда вызывается:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([1, 2, 3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такой вызов сопоставляется со вторым клаузом, с головой H=1 и T=[2, 3]. Это приводит к выводу на печать 1, затем рекурсивно вызывается write_a_list с аргументом в виде хвоста списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([2, 3]).&lt;br /&gt;
  /* Это вызов write_a_list(T). */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот второй вызов опять сопоставляется со вторым клаузом, где, на этот раз H=2 и T=[3], поэтому выводится 2 и опять рекурсивно вызывается write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([3]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С каким клаузом теперь такой вызов сопоставлятся? Напомним, что, хотя список [3] имеет всего один элемент, у него есть голова и хвост - голова есть 3, а хвост есть []. Таким образом, этот вызов опять сопоставляется со вторым клаузом с H=3 и T=[]. Теперь выводится 3 и  вызывается рекурсивно write_a_list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::write_a_list([]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь становится понятно для чего нужен первый клауз. Второй клауз не может быть сопоставлен с таким вызовом, поскольку [] не может быть разделен на голову и хвост. Если бы первого клазуа здесь не было бы, то выполнение goal оказалось бы неуспешным. Но, поскольку он есть, то первый клауз сопоставляется с вызовом и выполнение goal успешно завершается и нечего более не делается.&lt;br /&gt;
&lt;br /&gt;
===Подсчет элементов в Списке===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь, как подсчитать число элементов в списке, или какова длина списка? Логично определить:&lt;br /&gt;
&lt;br /&gt;
*Длина пустого списка [] есть 0.&amp;lt;br /&amp;gt;&lt;br /&gt;
*Длина любого другого списка есть 1 плюс длина его хвоста.&lt;br /&gt;
&lt;br /&gt;
Можно ли это запрограммировать? На Прологе это очень просто. Всего два клауза:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], 0).&lt;br /&gt;
  length_of([_|T], L):-&lt;br /&gt;
    length_of(T, TailLength),&lt;br /&gt;
    L = TailLength + 1.&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L),&lt;br /&gt;
  stdio::write(L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотрите прежде всего на второй клауз. Строго говоря, [_|T] сопоставляется с любым непустым списком, связывая T с хвостом списка. Значение головы неважно, если она есть, она может быть учтена как один элемент.&lt;br /&gt;
&lt;br /&gt;
Тогда вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется со вторым клаузом, с T=[2, 3]. Следующим шагом является вычисление длины хвоста T. Когда это сделано (не имеет значение, как), TailLength получит значение 2, и компьютер теперь может добавить 1 к ней и связать L со значением 3. Как выполняется этот промежуточный шаг? Надо найти длину списка [2, 3], путем удовлетворения цели&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([2, 3], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Другими словами, length_of вызывает себя рекурсивно. Этот вызов сопоставляется со вторым клаузом, связывая&lt;br /&gt;
&lt;br /&gt;
*[3] и T в вызове клаузы и&lt;br /&gt;
*TailLength с L в клаузе.&lt;br /&gt;
&lt;br /&gt;
Подчеркиваем, TailLength в вызове никак не пересекается с TailLength в клаузе, поскольку &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;каждый рекурсивный вызов клауза имеет собственный набор переменных&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Итак, теперь задача - найти длину списка [3], которая есть 1, и мы добавляем 1 к этому значению, чтобы получить длину списка [2, 3], что будет 2. Ну и хорошо!.&lt;br /&gt;
&lt;br /&gt;
Аналогично, length_of вызывает себя рекурсивно опять для получения длины списка [3]. Хвост  [3] есть [], поэтому T связвается с [], и задача теперь - получение длины списка [] и добавление к ней 1, что дает длину списка [3].&lt;br /&gt;
&lt;br /&gt;
Теперь все просто. Цель:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([], TailLength)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
сопоставляется с &amp;#039;&amp;#039;первым&amp;#039;&amp;#039; клаузом, связывая TailLength с 0. Поэтому теперь компьютер может добавить 1 к нему, получая длину списка [3], и возвращаясь теперь в вызывавший клауз. Это, в свою очередь, опять добавляет 1, давая длину списка [2, 3], и возвращается в клауз, который его вызывал; этот первоначальный клауз добавит снова 1, давая длину списка [1, 2, 3].&lt;br /&gt;
&lt;br /&gt;
Не растерялись? Мы надеемся, нет. В следующей короткой иллюстрации мы сводим воедино все вызовы. Мы использовали здесь прием подстрочника для того, чтобы показать, что аналогично называемые переменные в разных клаузах или различные вызовы того же самого клауза - одно и то же. &lt;br /&gt;
&amp;lt;vip&amp;gt;my::length_of([1, 2, 3], L1).&lt;br /&gt;
my::length_of([2, 3], L2).&lt;br /&gt;
my::length_of([3], L3).&lt;br /&gt;
my::length_of([], 0).&lt;br /&gt;
L3 =  0+1 = 1.&lt;br /&gt;
L2 = L3+1 = 2.&lt;br /&gt;
L1 = L2+1 = 3.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что Вам не нужно каждый раз создавать такого рода предикаты самостоятельно, Вы можете использовать готовый предикат &amp;#039;&amp;#039;&amp;#039;list::length&amp;#039;&amp;#039;&amp;#039; из PFC.&lt;br /&gt;
&lt;br /&gt;
===Хвостовая рекурсия===&lt;br /&gt;
&lt;br /&gt;
Вы, очевидно, заметили, что length_of не является (и не может быть) предикатом с хвостовой рекурсией, поскольку рекурсивный вызов не является последним шагом в его клаузе. Возможно ли создать предикат, определяющий длину, так, чтобы он был предикатом с хвостовой рекурсией? Да, но это потребует некоторых усилий.&lt;br /&gt;
&lt;br /&gt;
Проблема с предикатом length_of в том, что длину списка нельзя вычислить до тех пор, пока не вычислена длина его хвоста. Но из этой ситуации есть выход. Нам потребуется предикат, вычисляющий длину списка, с тремя аргументами.&lt;br /&gt;
&lt;br /&gt;
*Один из них - это список, от которого компьютер будет откусывать по одному элементу на каждом вызове до тех пор, пока этот список, как и прежде, не превратится в пустой список.&lt;br /&gt;
*Второй - это свободный аргумент, который в конечном итоге вернет результат (длину).&lt;br /&gt;
*Третий - это счетчик, значение которого начинается с нуля и увеличивается с каждым вызовом.&lt;br /&gt;
&lt;br /&gt;
Когда список в конечном итоге станет пустым, мы проунифицируем счетчик с несвязанным результатом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  length_of : (A*, integer, integer) procedure(i,o,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  length_of([], Result, Result).&lt;br /&gt;
  length_of([_|T], Result, Counter):-&lt;br /&gt;
    NewCounter = Counter + 1,&lt;br /&gt;
    length_of(T, Result, NewCounter).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::length_of([1, 2, 3], L, 0), /* Начинаем со счетчиком Counter = 0 */&lt;br /&gt;
  stdio::write(&amp;quot; L = &amp;quot;, L).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта версия предиката length_of более сложная и во многих смыслах менее логичная, чем предыдущая. Мы ее представили здесь главным образом для того, чтобы показать, что на практике &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;вы можете часто построить алгоритм с хвостовой рекурсией для задач, которые на первый взгляд требуют рекурсии другого типа&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
===Модификация Списка===&lt;br /&gt;
&lt;br /&gt;
Иногда требуется создать другой список из заданного списка. Это делается путем просмотра списка, элемент за элементом, заменяя каждый элемент вычисленным значением. Например, как эта программа, которая добавляет 1 к каждому элементу исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  add1 : (integer*, integer*) procedure(i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  add1([], [])./* граничное условие */&lt;br /&gt;
  add1([Head|Tail],[Head1|Tail1]):- /* отделяем голову от остального списка*/&lt;br /&gt;
    Head1 = Head+1, /* добавляем 1 к элементу-голове */&lt;br /&gt;
    add1(Tail, Tail1)./* далаем это с остальной частью списка*/&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::add1([1,2,3,4], NewList),&lt;br /&gt;
  stdio::write(NewList)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На обычном языке это звучит так:&lt;br /&gt;
*Добавление 1 ко всем элементам пустого списка порождаем пустой список,&lt;br /&gt;
*Для добавления 1 ко всем элемента любого другого списка:&lt;br /&gt;
**добавить 1 к голове и сделать эту голову головой результирующего списка, а затем&lt;br /&gt;
**добавить 1 к каждому элемента хвоста и этот хвост сделать хвостом результата.&lt;br /&gt;
&lt;br /&gt;
Загрузим программу и выполним такую цель&lt;br /&gt;
&amp;lt;vip&amp;gt;add1([1,2,3,4], NewList).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель вернет&lt;br /&gt;
&amp;lt;vip&amp;gt;NewList=[2,3,4,5]&lt;br /&gt;
1 Solution&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Опять о хвостовой рекурсии===&lt;br /&gt;
&lt;br /&gt;
Является ли предикат add1 проедикатом с хвостовой рекурсией? &lt;br /&gt;
Если у Вас есть опыт использования Lisp или Pascal, Вы могли бы подумать, что нет, поскольку Вы бы рассуждали так:&lt;br /&gt;
&lt;br /&gt;
*Делим список на &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;.&lt;br /&gt;
*Добавляем 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавляя 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, получаем &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
*Соединяем &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, что дает результирующий список.&lt;br /&gt;
&lt;br /&gt;
Это не похоже на хвостовую рекурсию, поскольку последний шаг - не рекурсивный вызов.&lt;br /&gt;
&lt;br /&gt;
Однако, и это важно, – &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Это не то, что делает Пролог&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;. В Visual Prolog add1 является предикатом с хвостовой рекурсией, поскольку выполняется в действительности следующим образом:&lt;br /&gt;
&lt;br /&gt;
*Связать голову и хвост исходного списка с &amp;#039;&amp;#039;Head&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, соответственно.&lt;br /&gt;
*Связать голову и хвост результирующего списка с &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;, соответственно. (&amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; пока не получили значений.)&lt;br /&gt;
*Добавить 1 к &amp;#039;&amp;#039;Head&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039;.&lt;br /&gt;
*Рекурсивно добавить 1 ко всем элементам списка &amp;#039;&amp;#039;Tail&amp;#039;&amp;#039;, что дает &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Когда это сделано, &amp;#039;&amp;#039;Head1&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Tail1&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;уже являются&amp;#039;&amp;#039;&amp;#039; головой и списком результата и отдельной операции по их соединению нет. Поэтому рекурсивный вызов и является последним шагом.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h6&amp;gt;Снова Модификация Списков&amp;lt;/h6&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Конечно, не всегда модификации подлежит каждый элемент. Посмотрим на программу, которая сканирует список чисел и копирует его, удаляя отрицательные числа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
predicates&lt;br /&gt;
  discard_negatives : (integer*, integer*) procedure(i,o). /*удалить отрицательные*/&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  discard_negatives([], []).&lt;br /&gt;
  discard_negatives([H|T], ProcessedTail):-&lt;br /&gt;
    H &amp;lt; 0,&lt;br /&gt;
    !, /* Если H отрицательно, пропускаем его */&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
  discard_negatives([H|T], [H|ProcessedTail]):-&lt;br /&gt;
    discard_negatives(T, ProcessedTail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::discard_negatives ([2, -45, 3, 468], X),&lt;br /&gt;
  stdio::write(X).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напрмер, цель&lt;br /&gt;
&amp;lt;vip&amp;gt;my::discard_negatives([2, -45, 3, 468], X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
дает&lt;br /&gt;
&amp;lt;vip&amp;gt;X=[2, 3, 468].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
А вот - предикат который копирует элементы списка, добавляя для каждого элемента его дубликат:&lt;br /&gt;
&amp;lt;vip&amp;gt;doubletalk([], []).&lt;br /&gt;
doubletalk([H|T], [H, H|DoubledTail]) :-&lt;br /&gt;
  doubletalk(T, DoubledTail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Принадлежнось списку===&lt;br /&gt;
&lt;br /&gt;
Допустим, имеется список с именами &amp;#039;&amp;#039;John&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Leonard&amp;#039;&amp;#039;, &amp;#039;&amp;#039;Eric&amp;#039;&amp;#039; и &amp;#039;&amp;#039;Frank&amp;#039;&amp;#039; и требуется, используя Visual Prolog, выяснить, принадлежит ли заданное имя этому списку. Другими словами, надо определить &amp;quot;отношение&amp;quot; между двумя аргументами: именем и списком имен. Это соответствует предикату&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember : (name, name*).&lt;br /&gt;
  /* &amp;quot;name&amp;quot; принадлежит списку &amp;quot;name*&amp;quot; */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В программе e01.pro первый клауз исследует голову списка. Если голова списка совпадает с искомым именем, то можно сделать заключение, что Name принадлежит списку. Поскольку хвост списка нас не интересует, то это мы представляем анонимной переменной. Благодаря первому клаузу, цель&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;])&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
удовлетворена.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Программа e01.pro */&lt;br /&gt;
class my&lt;br /&gt;
predicates&lt;br /&gt;
  isMember : (A, A*) determ.&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  isMember(Name, [Name|_]) :-&lt;br /&gt;
    !.&lt;br /&gt;
  isMember(Name, [_|Tail]):-&lt;br /&gt;
    isMember(Name,Tail).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::isMember(&amp;quot;john&amp;quot;, [&amp;quot;john&amp;quot;, &amp;quot;leonard&amp;quot;, &amp;quot;eric&amp;quot;, &amp;quot;frank&amp;quot;]),&lt;br /&gt;
  !,&lt;br /&gt;
  stdio::write(&amp;quot;Success&amp;quot;)&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если голова списка не есть Name, то надо исследовать, не содержится ли Name в хвосте списка.&lt;br /&gt;
&lt;br /&gt;
На обычном языке:&lt;br /&gt;
&lt;br /&gt;
Name принадлежит списку, если Name является первым элементом списка, или&amp;lt;br /&amp;gt;&lt;br /&gt;
Name принадлежит списку, если Name принадлежит хвосту.&lt;br /&gt;
&lt;br /&gt;
Второй клауз предиката isMember относится к этому отношению. Таким образом на Visual Prolog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;isMember(Name, [_|Tail]) :-&lt;br /&gt;
    isMember(Name, Tail).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Добавление списка к другому списку: декларативное и рекурсоивное решения===&lt;br /&gt;
&lt;br /&gt;
Рассмотренный предикат member программы e01.pro работает в двух направлениях. Вернемся к его клаузам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
member(Name, [Name|_]).&lt;br /&gt;
member(Name, [_|Tail]) :-&lt;br /&gt;
  member(Name, Tail).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На эти клаузы можно смотреть с двух различных точек зрения: декларативной и процедурной.&lt;br /&gt;
&lt;br /&gt;
*С декларативной точки зрения, клаузы выражают: &lt;br /&gt;
**Name (Имя) принадлежит списку, если голова списка есть Name&lt;br /&gt;
**иначе Name принадлежит списку, если оно (Имя) принадлежит хвосту.&lt;br /&gt;
*С процедурной точки зрения, эти же два клауза могут быть интерепретированы так:&amp;lt;br/&amp;gt;Чтобы найти элемент списка &lt;br /&gt;
**Найдите его голову&lt;br /&gt;
**иначе найдите элемент хвоста списка.&lt;br /&gt;
&lt;br /&gt;
Эти две точки зрения соответствуют целям&lt;br /&gt;
&amp;lt;vip&amp;gt;member(2, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;member(X, [1, 2, 3, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате первый вызов поручает Visual Prolog(у) проверить, истино ли нечто (принадлежность числа 2 списку [1,2,3,4]). Второй вызов поручает Visual Prolog(у) найти все члены списка [1,2,3,4]. Не смущайтесь этим. Предикат member является одним и тем же, но на его поведение можно смотреть под разными углами.&lt;br /&gt;
&lt;br /&gt;
====Рекурсия с процедуральной точки зрения====&lt;br /&gt;
&lt;br /&gt;
Прелесть Пролога заключается в том, что часто, когда мы конструируем клаузы для предиката, будучи на одной точке зрения, они будут работать и при взгляде с другой точки зрения. Чтобы обнаружить эту дуальность, мы приведем пример предиката для добавления (append) одного списка к другому. Мы определяем предикат append с тремя аргументами:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append(List1, List2, List3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот предикат интегрирует списки List1 и List2 в форму списка List3 так, что список List2 дописывается в конце списка List1. То есть содержательно - осуществляется добавление списка List2 к списку LIst1. Опять мы используем рекурсию (на этот раз с процедуральной точки зрения).&lt;br /&gt;
&lt;br /&gt;
Если список List1 пустой, результатом добавления списка List1 к списку List2 будет тот же самый List2. Запишем это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([], List2, List2).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если список List1 не пустой, то можно преобразовать списки List1 и List2 к форме списка &amp;#039;&amp;#039;List3&amp;#039;&amp;#039;, сделав голову списка List1 головой списка List3. В приведенном коде переменная H используется в качестве головы как списка List1, так и списка List3. Хвось списка List3 есть список L3, который составлен из остатка списка List1 (а именно, L1) и всего списка List2. Опять выразим это на Прологе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;append([H|L1], List2, [H|L3]) :-&lt;br /&gt;
   append(L1, List2, L3).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append работает следующим образом: пока список List1 не пустой, рекурсивное правило дописывает один элемент каждый раз к списку List3. Когда список List1 становится пустым, первый клауз  clause обеспечивает дописыванеие списка List2 в конец списка List3.&lt;br /&gt;
&lt;br /&gt;
====Варианты использования одного предиката====&lt;br /&gt;
&lt;br /&gt;
Подходя к предикату append с декларативной точки зрения, мы определили его как отношение между тремя списками. Это отношение справедливо также, если списки List1 и List3 известны, а List2 - нет. Более того, это также работает, если только List3 известен. Например, для того, чтобы выяснить, какие два списка могли бы быть соединены для получения известного списка, можно использовать вызов в форме&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, L2, [1, 2, 4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С таким целевым вызовом, Visual Prolog найдет следующие решения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
L1=[], L2=[1,2,4]&lt;br /&gt;
L1=[1], L2=[2,4]&lt;br /&gt;
L1=[1,2], L2=[4]&lt;br /&gt;
L1=[1,2,4], L2=[]&lt;br /&gt;
4 Solutions&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно использовать предикат append для нахождения списка, который следовало бы добавить к списку [3,4] для получения списка[1,2,3,4]. Попробуем такой вызов&lt;br /&gt;
&amp;lt;vip&amp;gt;append(L1, [3,4], [1,2,3,4]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Visual Prolog находит решение&lt;br /&gt;
&amp;lt;vip&amp;gt;L1=[1,2].&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предикат append определяет отношение между &amp;#039;&amp;#039;входным набором (input set)&amp;#039;&amp;#039; и &amp;#039;&amp;#039;выходным набором (output set)&amp;#039;&amp;#039; таким образом, что отношение применимо в обе стороны. При таком отношении возникает вопрос&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Что является выходным набором для заданного входного?&amp;#039;&amp;#039; или &amp;#039;&amp;#039;Какой входной набор соответствует заданному выходному?&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Статус аргументов данного вызова предиката известен как &amp;#039;&amp;#039;поток (или шаблон) ввода-вывода&amp;#039;&amp;#039;. Аргумент, который связан или наследуется в момент вызова является входным аргументом и обозначается как (i). Свободный аргумент является выходным аргументом и обозначается как (o).&lt;br /&gt;
&lt;br /&gt;
Предикат append обладает свойством поддерживать любой шаблон ввода-вывода, какой требуется. Однако не все предикаты имеют возможность вызова с различными шаблонами ввода-вывода. Когда клауз Пролога способен поддерживать множество шаблонов ввода-вывода, он назвается инверсным клаузом. Целый набор предикатов для обработки списков содержится в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
==Все Решения Сразу==&lt;br /&gt;
&lt;br /&gt;
Откаты и рекурсии являются двумя способами выполнения повторяющихся процессов. Рекурсия предпочтительнее, поскольку, в отличие от отката, позволяет передать данные через аргументы от одного рекурсивного вызова к следующему. Благодяря этому, рекурсивные процедуры могут использовать промежуточные результаты или счетчики по ходу выполнения.&lt;br /&gt;
&lt;br /&gt;
Однако есть одна вещь, которую, в отличие от рекурсии, могут делать откаты, а именно – искать все альтернитивные решения посредством одного вызова. Поэтому Вы можете оказаться в затрудинтельном положении: Вам нужно получить все решения за один вызов, и, при этом, они Вам нужны все сразу, как часть единой интегрированной структуры данных. Что делать?&lt;br /&gt;
&lt;br /&gt;
К счастью, Visual Prolog позволяет найти выход из этого положения. Предопределенная конструкция обработки списков получает целевой вызов в качестве своего аргумента и собирает все решиния для этого вызова в единый выходной список. Такаая конструкция для списков имеет два аргумента:&lt;br /&gt;
&lt;br /&gt;
*Первый аргумент, VarName, определяет аргумент в вызываемом целевом предикате, значения которого будут собираться в список.&lt;br /&gt;
*Второй - mypredicate - определяет предикат, который будет получать значения.&lt;br /&gt;
*Выходной параметр ListParam, является переменной, которая содержит список значений, полученных в ходе отката.&lt;br /&gt;
&lt;br /&gt;
Программа e02.pro использует обработку списка для вывода среднего возраста группы людей.&lt;br /&gt;
&amp;lt;vip&amp;gt;        /* Программа e02.pro */&lt;br /&gt;
class my&lt;br /&gt;
domains&lt;br /&gt;
  name = string.&lt;br /&gt;
  address = string.&lt;br /&gt;
  age = integer.&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  person : (name, address, age) nondeterm anyflow.&lt;br /&gt;
  sumlist : (age*, age, integer)procedure(i,o,o).&lt;br /&gt;
end class my&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  sumlist([],0,0).&lt;br /&gt;
  sumlist([H|T], Sum, N):-&lt;br /&gt;
    sumlist(T, S1, N1),&lt;br /&gt;
    Sum=H+S1, N=1+N1.&lt;br /&gt;
  &lt;br /&gt;
  person(&amp;quot;Sherlock Holmes&amp;quot;,&amp;quot;22B Baker Street&amp;quot;, 42).&lt;br /&gt;
  person(&amp;quot;Pete Spiers&amp;quot;,&amp;quot;Apt. 22, 21st Street&amp;quot;, 36).&lt;br /&gt;
  person(&amp;quot;Mary Darrow&amp;quot;,&amp;quot;Suite 2, Omega Home&amp;quot;, 51).&lt;br /&gt;
end implement my&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  L = [ Age || my::person(_, _, Age)],&lt;br /&gt;
  my::sumlist(L, Sum, N),&lt;br /&gt;
  Ave = Sum/N,&lt;br /&gt;
  stdio::write(&amp;quot;Average=&amp;quot;, Ave, &amp;quot;. &amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Клауз обработки списка в этой программе создает список L, который является списком всех возрастов, полученных от предиката person. Еслы бы потребовалось бы собрать список всех людей в возрасте 42 лет, можно было бы задать следующий цель-вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ Who || my::person(Who, _, 42) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий код порождает список всех положительных чисел исходного списка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;List = [ X || X = list::getMember_nd([2,-8,-3,6]), X &amp;gt; 0]&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Смешанные списки==&lt;br /&gt;
&lt;br /&gt;
Список целых объявляется просто&lt;br /&gt;
&amp;lt;vip&amp;gt;integer*&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же верно и для списков вещественных чисел (integer), символьных (symbol) списков или списков строк (string).&lt;br /&gt;
&lt;br /&gt;
Однако часто приходится хранить комбинацию различных типов элементов в составе одного и того же списка, такую как:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[2, 3, 5.12, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot;].&lt;br /&gt;
        /* Не корректно для Visual Prolog*/&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Смешанные списки&amp;#039;&amp;#039; - списки, содержащие элементы более чем одного типа. Для работы со списками разнотипных элементов должны использоваться специальные объявления, поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;одному и тому же&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; домену. Способом создания списка, который хранит такие различные типы элементов является использование функторов, поскольку домен может представляться &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;более, чем одним&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; функтором, каждый со своими т&amp;#039;&amp;#039;&amp;#039;и&amp;#039;&amp;#039;&amp;#039;повыми аргументами.&lt;br /&gt;
&lt;br /&gt;
Пример объаявления списка, который может хранить целые, символы, строки или списки таких  даных:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  /* функторами являются l, i, c, and s */&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Список&lt;br /&gt;
&amp;lt;vip&amp;gt;[ 2, 9, [&amp;quot;food&amp;quot;, &amp;quot;goo&amp;quot;], &amp;quot;new&amp;quot; ]&lt;br /&gt;
/* Не корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
на Visual Prolog следовало бы записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;[i(2), i(9), l([s(&amp;quot;food&amp;quot;), s(&amp;quot;goo&amp;quot;)]), s(&amp;quot;new&amp;quot;)]&lt;br /&gt;
        /* Корректно для Visual Prolog */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример предиката append показывает как использовать такого рода декларации в стандартных программах манипулирования списками.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  llist = l(list); i(integer); c(char); s(string).&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
  append : (A*,A*,A*) procedure (i,i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
clauses&lt;br /&gt;
  append([], L, L).&lt;br /&gt;
  append([X|L1], L2, [X|L3]):-&lt;br /&gt;
    append(L1, L2, L3).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::s(&amp;quot;likes&amp;quot;),my::l([my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;mary&amp;quot;)])],&lt;br /&gt;
    [my::s(&amp;quot;bill&amp;quot;), my::s(&amp;quot;sue&amp;quot;)], &lt;br /&gt;
    Ans&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Первый список: &amp;quot;, Ans,&amp;quot;\n\n&amp;quot;),&lt;br /&gt;
  my::append&lt;br /&gt;
    (&lt;br /&gt;
    [my::l([my::s(&amp;quot;This&amp;quot;),my::s(&amp;quot;is&amp;quot;),my::s(&amp;quot;a&amp;quot;),my::s(&amp;quot;list&amp;quot;)]),my::s(&amp;quot;bee&amp;quot;)], &lt;br /&gt;
    [my::c(&amp;#039;c&amp;#039;)], &lt;br /&gt;
    Ans2&lt;br /&gt;
    ),&lt;br /&gt;
  stdio::write(&amp;quot;Второй списко: &amp;quot;, Ans2, &amp;quot;\n\n&amp;amp;quot).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Разбор с использованием списков==&lt;br /&gt;
&lt;br /&gt;
Ниже приведена программа, демонстрирующая [[wikipedia:parsing|разбор]] (parsing)с использованием списков. Процесс разбора работает в методом сворачивания. В этом примере входная строка преобразуется в структуру данных Пролога, которая может быть использована далее.&lt;br /&gt;
&lt;br /&gt;
Этот разборщик предназначен для примитивного формального языка. Хотя этот пример достаточно сложен с точки зрения этого руководства, мы решили поместит его здесь, поскольку разбор является одной из областей, где Visual Prolog очень эффективен. Если Вы чувствуете себя не вполне готовым для этого раздела, Вы можете этот пример пропустить и продолжить чтение руководства без потери содержательности.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;#include @&amp;quot;pfc\exception\exception.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\string\string.ph&amp;quot;&lt;br /&gt;
#include @&amp;quot;pfc\console\console.ph&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class my_t&lt;br /&gt;
predicates&lt;br /&gt;
  tokl : (string, string*) procedure (i,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_t&lt;br /&gt;
clauses&lt;br /&gt;
  tokl(Str, [H|T]) :-&lt;br /&gt;
    string::fronttoken(Str, H, Str1),&lt;br /&gt;
    !,&lt;br /&gt;
    tokl(Str1, T).&lt;br /&gt;
  tokl(_, []).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
/* * * * * * * * * * * * * * * * * * * *  *&lt;br /&gt;
* Вторая часть этой программы - разборщик *&lt;br /&gt;
* * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
class my_p&lt;br /&gt;
domains&lt;br /&gt;
  program = program(statement*).&lt;br /&gt;
  /* * * * * * * * * * * * * * * * * * * * *  * &lt;br /&gt;
  * Определение что есть языковая конструкция *&lt;br /&gt;
  * (предложение языка)                       *&lt;br /&gt;
  * * * * * * * * * * * * * * * * * * * * * * */&lt;br /&gt;
  statement =&lt;br /&gt;
    if_Then_Else(exp, statement, statement);&lt;br /&gt;
    if_Then(exp, statement);&lt;br /&gt;
    while(exp, statement);&lt;br /&gt;
    assign(id, exp).&lt;br /&gt;
  /* * * * * * * * * * *  *&lt;br /&gt;
  * Определение выражения *&lt;br /&gt;
  * * * * * * * *  * * *  */&lt;br /&gt;
  exp = &lt;br /&gt;
    plus(exp, exp);&lt;br /&gt;
    minus(exp, exp);&lt;br /&gt;
    var(id);&lt;br /&gt;
    int(integer).&lt;br /&gt;
    id = string.&lt;br /&gt;
&lt;br /&gt;
  predicates&lt;br /&gt;
    s_program : (string*, program) procedure (i,o).&lt;br /&gt;
    s_statement : (string*, string*, statement) determ (i,o,o).&lt;br /&gt;
    s_statement_list : (string*, string*, statement*) determ (i,o,o).&lt;br /&gt;
    s_exp : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
    s_exp1 : (string*, string*, exp, exp) determ (i,o,i,o).&lt;br /&gt;
    s_exp2 : (string*, string*, exp) determ (i,o,o).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my_p&lt;br /&gt;
clauses&lt;br /&gt;
  s_program(TokenList, program(StatementList)):-&lt;br /&gt;
    s_statement_list(TokenList, _, StatementList),&lt;br /&gt;
    !.&lt;br /&gt;
  s_program(_, program([])).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement_list([], [], []) :- !.&lt;br /&gt;
  s_statement_list(List1, List4, [Statement|Program]) :-&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;;&amp;quot;|List3],&lt;br /&gt;
    s_statement_list(List3, List4, Program).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List7,if_then_else(Exp,Statement1, Statement2)):-&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement1),&lt;br /&gt;
    List4=[&amp;quot;else&amp;quot;|List5],!,&lt;br /&gt;
    s_statement(List5, List6, Statement2),&lt;br /&gt;
    List6=[&amp;quot;fi&amp;quot;|List7].&lt;br /&gt;
  s_statement([&amp;quot;if&amp;quot;|List1], List5,if_then(Exp, Statement)) :- !,&lt;br /&gt;
    s_exp(List1, List2, Exp),&lt;br /&gt;
    List2=[&amp;quot;then&amp;quot;|List3],&lt;br /&gt;
    s_statement(List3, List4, Statement),&lt;br /&gt;
    List4=[&amp;quot;fi&amp;quot;|List5].&lt;br /&gt;
  s_statement([&amp;quot;do&amp;quot;|List1], List4,while(Exp, Statement)) :- !,&lt;br /&gt;
    s_statement(List1, List2, Statement),&lt;br /&gt;
    List2=[&amp;quot;while&amp;quot;|List3],&lt;br /&gt;
    s_exp(List3, List4, Exp).&lt;br /&gt;
  s_statement([ID|List1], List3,assign(Id,Exp)) :-&lt;br /&gt;
    string::isname(ID),&lt;br /&gt;
    List1=[&amp;quot;=&amp;quot;|List2],&lt;br /&gt;
    s_exp(List2, List3, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp(List1, List3, Exp):-&lt;br /&gt;
    s_exp2(List1, List2, Exp1),&lt;br /&gt;
    s_exp1(List2, List3, Exp1, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp1([&amp;quot;+&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, plus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1([&amp;quot;-&amp;quot;|List1], List3, Exp1, Exp) :- !,&lt;br /&gt;
    s_exp2(List1, List2, Exp2),&lt;br /&gt;
    s_exp1(List2, List3, minus(Exp1, Exp2), Exp).&lt;br /&gt;
  s_exp1(List, List, Exp, Exp).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  s_exp2([Int|Rest], Rest, int(I)) :-&lt;br /&gt;
    trap(I = toTerm(Int),Error,exception::clear_fail(Error)),&lt;br /&gt;
    !.&lt;br /&gt;
  s_exp2([Id|Rest], Rest, var(Id)) :-&lt;br /&gt;
    string::isname(Id).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  stdio::write(Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res),&lt;br /&gt;
  stdio::write(Res).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Загрузите программу и выполните целевой вызов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;goal&lt;br /&gt;
  my_t::tokl(&amp;quot;b=2; if b then a=1 else a=2 fi; do a=a-1 while a;&amp;quot;, Ans),&lt;br /&gt;
  my_p::s_program(Ans, Res).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Prolog вернет структуру программы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;Ans = [&amp;quot;b&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;if&amp;quot;,&amp;quot;b&amp;quot;,&amp;quot;then&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;1&amp;quot;,&lt;br /&gt;
    &amp;quot;else&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;fi&amp;quot;,&amp;quot;;&amp;quot;,&amp;quot;do&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;=&amp;quot;,&amp;quot;a&amp;quot;,&lt;br /&gt;
    &amp;quot;-&amp;quot;,&amp;quot;1&amp;quot;,&amp;quot;while&amp;quot;,&amp;quot;a&amp;quot;,&amp;quot;;&amp;quot;],&lt;br /&gt;
Res=program&lt;br /&gt;
  (&lt;br /&gt;
  [assign(&amp;quot;b&amp;quot;,int(2)),&lt;br /&gt;
   if_then_else&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;b&amp;quot;),&lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(1)), &lt;br /&gt;
     assign(&amp;quot;a&amp;quot;,int(2))&lt;br /&gt;
     ),&lt;br /&gt;
   while&lt;br /&gt;
     (&lt;br /&gt;
     var(&amp;quot;a&amp;quot;),&lt;br /&gt;
     assign&lt;br /&gt;
       (&lt;br /&gt;
       &amp;quot;a&amp;quot;,&lt;br /&gt;
       minus&lt;br /&gt;
         (&lt;br /&gt;
         var(&amp;quot;a&amp;quot;),&lt;br /&gt;
         int(1)&lt;br /&gt;
         )&lt;br /&gt;
       )&lt;br /&gt;
     )&lt;br /&gt;
  ])&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Преобразование в этом примере представлено двумя шагами: сканирование и разбор. Предикат tokl  - это сканер. Он принимает строку и преобразует ее в список токенов. Все предикаты с именами, начинающимися с s_ являются предикатами парсера. В этом примере входный текст представляет программу на языке, подобном языку Паскаль, и состоящей из Паскале-подобных предложений. Этот язык программирования позволяет только предложения вида: IF THEN ELSE, IF THEN, DO WHILE и присваивание (ASSIGNMENT). Предложения состоят из выражений и вложенных предложений. Выражения - сложение, вычитание, переменные и целые.&lt;br /&gt;
&lt;br /&gt;
Далее, как это работает:&lt;br /&gt;
&lt;br /&gt;
*Первый клауз парсера, s_program, принимает список токенов и пытается преобразовать его в список предложений.&lt;br /&gt;
*Предикат s_statement_list принимает тот же самый список токенов и проверяет возможность деления токенов на предложения, завершающиеся точкой с запятой.&lt;br /&gt;
*Предикат s_statement проверяет могут ли начальные токены списка (токенов) представлять собой правильное предложение. Если да, то такое предложение возвращается в виде структуры, а остаток токенов передается рекурсивно вызываемому предикату s_statement_list.&lt;br /&gt;
*Четыре клауза предиката s_statement соответствуют четырем типам, которые парсер понимает.&amp;lt;br\&amp;gt; Если первый клауз предиката s_statement не может преобразовать список токенов в предложение вида IF THEN ELSE, то клауз завершается неуспешно и в порядке отката переходит к следующему клаузу предиката s_statement. Теперь делается попытка преобразовать список токенов в конструкцию вида IF THEN. Если эта попытка неуспешна, то в следующем клаузе делается попытка преобразования к предложению вида DO WHILE.&lt;br /&gt;
*Если первые три клауза предиката s_statement завершаются неуспешно, то последний клауз этого предиката провереяет представлена ли в списке токенов операция присваивания. Этот клауз проверяет &amp;quot;на присваивание&amp;quot; является ли первый терм символом, второй - знаком равно (&amp;quot;=&amp;quot;), а следующие термы представляют простое математическое выражение.&lt;br /&gt;
*Предикаты s_exp, s_exp1 и s_exp2 работают аналогично, путем проверки являются ли начальные термы выражениями и, если это так, то предикату s_statement предикат s_exp возвращает остаток термов и математическое выражение в виде структуры.&lt;br /&gt;
&lt;br /&gt;
==Заключение==&lt;br /&gt;
&lt;br /&gt;
В этом руководстве раскрыта следующие важные моменты:&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;Списки&amp;#039;&amp;#039; могут содержать произвольное число элементов; Вы объявляете их простым добавление звездочки (&amp;#039;&amp;#039;&amp;#039;*&amp;#039;&amp;#039;&amp;#039;) в конце ранее определенного домена.&lt;br /&gt;
*Список является рекурсивным составным объектом, состоящим из головы и хвоста. Голова есть первый элемент, а хвост - остальная часть списка (без первого элемента). Хвост списка - всегда список; голова списка - всегда элемента. Список может содержать ноль или более элементов; Пустой список обозначается [].&lt;br /&gt;
*Элементами списка можже быть что угодно, включая другиме списки; все элементы списка должны принадлежать одному домену. В этом случае объявление домена элементов должно выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  element_list = elements*.&lt;br /&gt;
  elements = ....&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где elements = один из стандартных доменов (integer, real, etc.) или набор альтернатив  обозначенных различными функторами (int(integer); rl(real); smb(symbol); и т.д.). Смешение типов в списках языка системы Visual Prolog допускается только включением их в составные объекты или функторы.&lt;br /&gt;
*Можно использовать разделители (запятые, [ и |) для явного отделения головы списка от хвоста. Так, список&lt;br /&gt;
&amp;lt;vip&amp;gt;[a, b, c, d]&amp;lt;/vip&amp;gt;&lt;br /&gt;
может быть записан как:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
[a|[b, c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b|[c, d]] &lt;br /&gt;
или&lt;br /&gt;
[a, b, c|[d]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c, d]]] &lt;br /&gt;
или&lt;br /&gt;
[a|[b|[c|[d]]]] &lt;br /&gt;
или даже&lt;br /&gt;
[a|[b|[c|[d|[]]]]]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Обработка списков заключается в рекурсивном отщеплении головы списка (и выполнении действий над ней) до опустошения списка.&lt;br /&gt;
*Предикаты для работы со списками содержаться в классе &amp;#039;&amp;#039;&amp;#039;list&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
*Visual Prolog поддерживает встроенную конструкцию обработки списков, которая принимает целевой предикат в качестве одного из аргументов и собирает все решения этого целевого предиката в едином списке. Синтаксис такой конструкции&lt;br /&gt;
&amp;lt;vip&amp;gt;Result = [ Argument || myPredicate(Argument) ]&amp;lt;/vip&amp;gt;&lt;br /&gt;
*Поскольку Visual Prolog требует, чтобы все элементы списка принадлежали бы одному и тому же домену, следует использовать функторы для создания списков, хранящих элементы различного типа.&lt;br /&gt;
*процесс &amp;#039;&amp;#039;разбора с использованием разностных списков (parsing by difference lists)&amp;#039;&amp;#039; работает путем сокращения задачи; пример в этом руководстве преобрахует строку входных данных в структуру, которая может обрабатываться позже.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[[en:Lists and Recursion]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=ProfileCount&amp;diff=1507</id>
		<title>ProfileCount</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=ProfileCount&amp;diff=1507"/>
		<updated>2007-11-20T19:47:43Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для измерения времени выполнения предикатов можно использовать класс &amp;#039;&amp;#039;&amp;#039;profileCount&amp;#039;&amp;#039;&amp;#039; из пакета &amp;#039;&amp;#039;&amp;#039;pfc\profile&amp;#039;&amp;#039;&amp;#039;. Перед его использованием необходимо добавить опцию компилятора &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039; и перестроить проект. Также необходимо вставить вызовы запускающие профилирование &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039;, обычно при инициализации приложения, останова &amp;#039;&amp;#039;&amp;#039;stop&amp;#039;&amp;#039;&amp;#039; и вывода результатов &amp;#039;&amp;#039;&amp;#039;print&amp;#039;&amp;#039;&amp;#039; (обычно в конце приложения). &lt;br /&gt;
&lt;br /&gt;
Вот типичный пример использования в консольном приложении:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
clauses&lt;br /&gt;
    run():-&lt;br /&gt;
        console::init(),&lt;br /&gt;
        profileCount::start(),        &lt;br /&gt;
        try&lt;br /&gt;
            главныйПредикат()&lt;br /&gt;
        finally        &lt;br /&gt;
            profileCount::stop(),&lt;br /&gt;
            Out = console::getConsoleOutputStream(),&lt;br /&gt;
            profileCount::print(Out)&lt;br /&gt;
        end try.&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для всех предикатов выводится количество их вызовов, а для некоторых и суммарное время выполнения. Следует учесть, что данный инструмент (как впрочем и все) влияет на выполняемую программу (даже если предикат &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039; не был выполнен). Программа становится больше и выполняется медленней. Это надо учитывать при анализе полученных результатов. {{note|picsize=30|content=И, самое главное, не забыть убрать опцию &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039; после, того, как все замеры будут произведены.}} Если модуль построен без опции &amp;#039;&amp;#039;&amp;#039;/PROFILE&amp;#039;&amp;#039;&amp;#039;, то предикат &amp;#039;&amp;#039;&amp;#039;start&amp;#039;&amp;#039;&amp;#039; не имеет никакого эффекта.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пример результат работы:&lt;br /&gt;
&lt;br /&gt;
 Predicate invocations count information:&lt;br /&gt;
    1.                           outputStream::write/0...        1   0.00017656&lt;br /&gt;
    2.                         bufferSupport::getBuffer/2        2   0.00014974&lt;br /&gt;
    3.                           outputStream::write/0...        1   0.00009526&lt;br /&gt;
    4.           outputStream_console::writeToStdDevice/4        2   0.00007683&lt;br /&gt;
    5.                              outputStream::flush/0        1   0.00007599&lt;br /&gt;
    6.               outputStream_console::bufferFullCB/2        2   0.00006397&lt;br /&gt;
    7.                                   list::filter/2-&amp;gt;        1   0.00003436&lt;br /&gt;
    8.                                      авто::new/1-&amp;gt;        3   0.00003241&lt;br /&gt;
    9.                             list::getMember_nd/1-&amp;gt;        6   0.00002123&lt;br /&gt;
   10.                          fileSystem_api::lf_crlf/4        2   0.00002039&lt;br /&gt;
   11.    outputStream_console::convertLF2CRLF_ifNeeded/4        2   0.00001062&lt;br /&gt;
   12.                                      main::run$1/0        3   0.00000726&lt;br /&gt;
   13.                  fileSystem_api::searchLineFeeds/6        2   0.00000698&lt;br /&gt;
   14.                                     авто::цена/0-&amp;gt;        4   0.00000670&lt;br /&gt;
   15.                                stream::getMode/0-&amp;gt;        4   0.00000615&lt;br /&gt;
   16.                                console::write/0...        1   0.00000615&lt;br /&gt;
   17.                                        авто::new/1        3   0.00000475&lt;br /&gt;
   18.         fileSystem_api::convertToCRLF_IfNeeded/4-&amp;gt;        2   0.00000419&lt;br /&gt;
   19.                      console_api::getStdHandle/1-&amp;gt;        2   0.00000391&lt;br /&gt;
   20.                        fileSystem_api::getSize/2-&amp;gt;        2   0.00000363&lt;br /&gt;
   21.                        outputSupport::doNotFlush/0        1   0.00000307&lt;br /&gt;
   22.                console::getConsoleOutputStream/0-&amp;gt;        1   0.00000251&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Категория:Profile]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%93%D0%B4%D0%B5_%D0%B4%D0%B5%D0%BA%D0%BB%D0%B0%D1%80%D0%B8%D1%80%D1%83%D1%8E%D1%82%D1%81%D1%8F_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B%3F&amp;diff=1353</id>
		<title>Где декларируются предикаты?</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%93%D0%B4%D0%B5_%D0%B4%D0%B5%D0%BA%D0%BB%D0%B0%D1%80%D0%B8%D1%80%D1%83%D1%8E%D1%82%D1%81%D1%8F_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B%3F&amp;diff=1353"/>
		<updated>2007-11-18T07:45:52Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: добавил ссылки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Обычно, говоря о Visual Prolog, подчеркивают, что это - типизированный язык. Это подразумевает, что существует объявление (или декларация):&lt;br /&gt;
*типов данных, используемых в предикатах в качестве входных или выходных параметров&lt;br /&gt;
*других характеристик предикатов&lt;br /&gt;
Это объявление используется затем в клаузе - исполняемой части предиката, где и интерпретируется как программистом, так и компилятором. &lt;br /&gt;
&lt;br /&gt;
Например, в следущем коде&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
clauses&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
декларация предиката (раздел &amp;#039;&amp;#039;&amp;#039;[[predicates]]&amp;#039;&amp;#039;&amp;#039;) и клауза (раздел &amp;#039;&amp;#039;&amp;#039;[[clauses]]&amp;#039;&amp;#039;&amp;#039;) помещаются рядом по тексту. Но это лишь частный случай возможного их взаимного размещения.&lt;br /&gt;
&lt;br /&gt;
Местом размещения исполняемой части &amp;#039;&amp;#039;&amp;#039;всегда&amp;#039;&amp;#039;&amp;#039; является имплементация класса (&amp;#039;&amp;#039;&amp;#039;implement ... end implement&amp;#039;&amp;#039;&amp;#039;). &lt;br /&gt;
&lt;br /&gt;
Местами помещения декларации могут быть:&lt;br /&gt;
*та же имплементация класса (&amp;#039;&amp;#039;&amp;#039;implement ... end implement&amp;#039;&amp;#039;&amp;#039;) &lt;br /&gt;
*декларация одноименного класса (&amp;#039;&amp;#039;&amp;#039;class ... end class&amp;#039;&amp;#039;&amp;#039;)&lt;br /&gt;
*декларация интерфейса, который квалифицирует одноименный класс (&amp;#039;&amp;#039;&amp;#039;interface ... end interface&amp;#039;&amp;#039;&amp;#039;)&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;В первом случае&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; декларация может размещаться как выше, так и ниже по тексту.&lt;br /&gt;
&lt;br /&gt;
Хорошим стилем считается, когда декларация помещена непосредственно над клаузой, как в примере выше. При этом имена разделов по-прежнему пишутся во множественном числе (&amp;#039;&amp;#039;&amp;#039;predicates&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;clauses&amp;#039;&amp;#039;&amp;#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Во втором случае&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; декларация предиката и его клауза связаны однозначно, поскольку однозначно общим именем связаны декларация класса и его имплементация. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
class обработкаФайла&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
end class обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
end implement обработкаФайла&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Следует подчеркнуть, что декларация класса - это сущность, неотделимая от имплементации класса с тем же именем. То есть нельзя в какую-либо произвольную декларацию класса поместить декларацию предиката, а в какую-то имплементацию класса с другим именем поместить клаузу и попытаться их связать какими-то ссылками, использующими эти имена. Имя класса однозначно определяет его декларацию и его имплементацию. Именно в них можно поместить декларацию предиката и его клаузу.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Третий способ&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; декларирования предиката связан с порождением объектов, поскольку только классы, имеющие интерфейс, могут быть использованы как генераторы объектов. Имя интерфейса в этом случае может не совпадать с именем класса и, соответственно, имплементации.&lt;br /&gt;
&lt;br /&gt;
Наш фрагмент кода будет выглядеть в этом случае иначе.&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
interface обработкаФайла&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
end interface обработкаФайла&lt;br /&gt;
&lt;br /&gt;
class обработкаФайла : обработкаФайла&lt;br /&gt;
end class обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
end implement обработкаФайла&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Обратите внимание, декларация класса (&amp;#039;&amp;#039;&amp;#039;class... end class&amp;#039;&amp;#039;&amp;#039;) не содержит ничего, кроме указателя на используемый интерфейс. Несмотря на это, ее нельзя исключить из кода и связать клаузу предиката с его декларацией каким-либо другим способом.&lt;br /&gt;
&lt;br /&gt;
Приведенный фрагмент кода мог бы выглядеть и по-другому (использование другого имени интерфейса)&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
interface fileHandling&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
end interface fileHandling&lt;br /&gt;
&lt;br /&gt;
class обработкаФайла : fileHandling&lt;br /&gt;
end class обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
end implement обработкаФайла&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Все три приведенные способа декларирования предикатов определяют различные свойства и приемы их использования в классах и объектах и поэтому не являются равнозначными альтернативами.&lt;br /&gt;
&lt;br /&gt;
Проперти (секция [[properties]]) так же могут реализовываться через клаузы, и тогда они водят неявное описание предиката(ов). Если есть (i) проперти, то клауза должна соответствовать предикату &amp;#039;&amp;#039;properyName : (propertyValue)&amp;#039;&amp;#039;, а для (o) - &amp;#039;&amp;#039;properyName : () -&amp;gt; propertyValue&amp;#039;&amp;#039;.&lt;br /&gt;
&amp;lt;vip&amp;gt;interface обработкаФайла&lt;br /&gt;
properties&lt;br /&gt;
  размерФайла : unsigned(0).&lt;br /&gt;
end interface обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
clauses&lt;br /&gt;
  размерФайла() = Размер :-&lt;br /&gt;
    Размер64 = fileSystem_api::getFileSize(handle_fv),&lt;br /&gt;
    unsigned64(Размер, _).&lt;br /&gt;
 &lt;br /&gt;
end implement обработкаФайла&amp;lt;/vip&amp;gt;&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%93%D0%B4%D0%B5_%D0%B4%D0%B5%D0%BA%D0%BB%D0%B0%D1%80%D0%B8%D1%80%D1%83%D1%8E%D1%82%D1%81%D1%8F_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B%3F&amp;diff=1352</id>
		<title>Где декларируются предикаты?</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%93%D0%B4%D0%B5_%D0%B4%D0%B5%D0%BA%D0%BB%D0%B0%D1%80%D0%B8%D1%80%D1%83%D1%8E%D1%82%D1%81%D1%8F_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B%3F&amp;diff=1352"/>
		<updated>2007-11-18T07:39:33Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: добавил проперти секцию&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Обычно, говоря о Visual Prolog, подчеркивают, что это - типизированный язык. Это подразумевает, что существует объявление (или декларация):&lt;br /&gt;
*типов данных, используемых в предикатах в качестве входных или выходных параметров&lt;br /&gt;
*других характеристик предикатов&lt;br /&gt;
Это объявление используется затем в клаузе - исполняемой части предиката, где и интерпретируется как программистом, так и компилятором. &lt;br /&gt;
&lt;br /&gt;
Например, в следущем коде&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
clauses&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
декларация предиката (раздел &amp;#039;&amp;#039;&amp;#039;predicates&amp;#039;&amp;#039;&amp;#039;) и клауза (раздел &amp;#039;&amp;#039;&amp;#039;clauses&amp;#039;&amp;#039;&amp;#039;) помещаются рядом по тексту. Но это лишь частный случай возможного их взаимного размещения.&lt;br /&gt;
&lt;br /&gt;
Местом размещения исполняемой части &amp;#039;&amp;#039;&amp;#039;всегда&amp;#039;&amp;#039;&amp;#039; является имплементация класса (&amp;#039;&amp;#039;&amp;#039;implement ... end implement&amp;#039;&amp;#039;&amp;#039;). &lt;br /&gt;
&lt;br /&gt;
Местами помещения декларации могут быть:&lt;br /&gt;
*та же имплементация класса (&amp;#039;&amp;#039;&amp;#039;implement ... end implement&amp;#039;&amp;#039;&amp;#039;) &lt;br /&gt;
*декларация одноименного класса (&amp;#039;&amp;#039;&amp;#039;class ... end class&amp;#039;&amp;#039;&amp;#039;)&lt;br /&gt;
*декларация интерфейса, который квалифицирует одноименный класс (&amp;#039;&amp;#039;&amp;#039;interface ... end interface&amp;#039;&amp;#039;&amp;#039;)&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;В первом случае&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; декларация может размещаться как выше, так и ниже по тексту.&lt;br /&gt;
&lt;br /&gt;
Хорошим стилем считается, когда декларация помещена непосредственно над клаузой, как в примере выше. При этом имена разделов по-прежнему пишутся во множественном числе (&amp;#039;&amp;#039;&amp;#039;predicates&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;clauses&amp;#039;&amp;#039;&amp;#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Во втором случае&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; декларация предиката и его клауза связаны однозначно, поскольку однозначно общим именем связаны декларация класса и его имплементация. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
class обработкаФайла&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
end class обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
end implement обработкаФайла&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Следует подчеркнуть, что декларация класса - это сущность, неотделимая от имплементации класса с тем же именем. То есть нельзя в какую-либо произвольную декларацию класса поместить декларацию предиката, а в какую-то имплементацию класса с другим именем поместить клаузу и попытаться их связать какими-то ссылками, использующими эти имена. Имя класса однозначно определяет его декларацию и его имплементацию. Именно в них можно поместить декларацию предиката и его клаузу.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Третий способ&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; декларирования предиката связан с порождением объектов, поскольку только классы, имеющие интерфейс, могут быть использованы как генераторы объектов. Имя интерфейса в этом случае может не совпадать с именем класса и, соответственно, имплементации.&lt;br /&gt;
&lt;br /&gt;
Наш фрагмент кода будет выглядеть в этом случае иначе.&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
interface обработкаФайла&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
end interface обработкаФайла&lt;br /&gt;
&lt;br /&gt;
class обработкаФайла : обработкаФайла&lt;br /&gt;
end class обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
end implement обработкаФайла&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Обратите внимание, декларация класса (&amp;#039;&amp;#039;&amp;#039;class... end class&amp;#039;&amp;#039;&amp;#039;) не содержит ничего, кроме указателя на используемый интерфейс. Несмотря на это, ее нельзя исключить из кода и связать клаузу предиката с его декларацией каким-либо другим способом.&lt;br /&gt;
&lt;br /&gt;
Приведенный фрагмент кода мог бы выглядеть и по-другому (использование другого имени интерфейса)&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
interface fileHandling&lt;br /&gt;
predicates&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной:(string ИмяФайла, unsigned ЧислоСимоволов) -&amp;gt;string.&lt;br /&gt;
end interface fileHandling&lt;br /&gt;
&lt;br /&gt;
class обработкаФайла : fileHandling&lt;br /&gt;
end class обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
  выделитьИзФайлаОтрезокТекстаСЗаданнойДлиной(ИмяФайла,ЧислоСимоволов)=ОтрезокТекста:-&lt;br /&gt;
    ПолныйТекстФайла=прочитатьФайл(ИмяФайла),&lt;br /&gt;
    ...&lt;br /&gt;
end implement обработкаФайла&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Все три приведенные способа декларирования предикатов определяют различные свойства и приемы их использования в классах и объектах и поэтому не являются равнозначными альтернативами.&lt;br /&gt;
&lt;br /&gt;
Проперти (секция properties) так же могут реализовываться через клаузы, и тогда они водят неявное описание предиката(ов). Если есть (i) проперти, то клауза должна соответствовать предикату &amp;#039;&amp;#039;properyName : (propertyValue)&amp;#039;&amp;#039;, а для (o) - &amp;#039;&amp;#039;properyName : () -&amp;gt; propertyValue&amp;#039;&amp;#039;.&lt;br /&gt;
&amp;lt;vip&amp;gt;interface обработкаФайла&lt;br /&gt;
properties&lt;br /&gt;
  размерФайла : unsigned(0).&lt;br /&gt;
end interface обработкаФайла&lt;br /&gt;
&lt;br /&gt;
implement обработкаФайла&lt;br /&gt;
clauses&lt;br /&gt;
  размерФайла() = Размер :-&lt;br /&gt;
    Размер64 = fileSystem_api::getFileSize(handle_fv),&lt;br /&gt;
    unsigned64(Размер, _).&lt;br /&gt;
 &lt;br /&gt;
end implement обработкаФайла&amp;lt;/vip&amp;gt;&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%93%D0%B4%D0%B5_%D0%B4%D0%B5%D0%BA%D0%BB%D0%B0%D1%80%D0%B8%D1%80%D1%83%D1%8E%D1%82%D1%81%D1%8F_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B%3F&amp;diff=1351</id>
		<title>Обсуждение:Где декларируются предикаты?</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%93%D0%B4%D0%B5_%D0%B4%D0%B5%D0%BA%D0%BB%D0%B0%D1%80%D0%B8%D1%80%D1%83%D1%8E%D1%82%D1%81%D1%8F_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B%3F&amp;diff=1351"/>
		<updated>2007-11-17T23:34:51Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: Новая: не совсем понятно, что такое декларация интерфейса, который квалифицирет одноименный класс (interface ... en...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;не совсем понятно, что такое&lt;br /&gt;
декларация интерфейса, который квалифицирет одноименный класс (interface ... end interface)&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1289</id>
		<title>Анонимные предикаты</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1289"/>
		<updated>2007-11-16T06:35:07Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Погружение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Анонимные предикаты - новая возможность компилятора, которая будет реализована начиная с версии 7.2&lt;br /&gt;
 &lt;br /&gt;
===Вступление===&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Vip&amp;gt;&lt;br /&gt;
interface авто&lt;br /&gt;
properties&lt;br /&gt;
    цена : unsigned(o).&lt;br /&gt;
end interface авто&lt;br /&gt;
&lt;br /&gt;
class выборАвтомобиля&lt;br /&gt;
predicates&lt;br /&gt;
    неДорогие : () -&amp;gt; авто* Результат.&lt;br /&gt;
...&lt;br /&gt;
implement выборАвтомобиля&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter({(Авто) :- Авто:цена &amp;lt;= 10000, !}, списокАвтомобилей).&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Казалось бы ничего нового, ведь то же самое можно записать при помощи дополнительного предиката. Например так&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    фильтрДляНедорогих : (авто Авто).&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрДляНедорогих(Авто) :- Авто:цена &amp;lt;= 10000, !.&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter(фильтрДляНедорогих, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но сегодня для нас сумма 10000 является пределом мечтаний, а завтра 12000, как быть? Есть два решения, или менять константу, или добавить факт-переменную. Анонимный предикат позволит сделать это изящней.&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    неДорогие2 : (unsigned НашиВозможности).&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие2(НашиВозможности) = list::filter({(Авто) :- Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если внимательно присмотреться, то тут мы наблюдаем картину, ранее недоступную невооружённым глазом. Переменная &amp;#039;&amp;#039;НашиВозможности&amp;#039;&amp;#039; из предиката &amp;#039;&amp;#039;&amp;#039;&amp;#039;неДорогие2&amp;#039;&amp;#039;&amp;#039;&amp;#039; перекочевала во внутрь другого предиката - фильтра! Вот это уже нельзя сделать использую дополнительный предикат, как раньше.&lt;br /&gt;
&lt;br /&gt;
===Погружение===&lt;br /&gt;
Давайте еще усложним наш пример, не сильно отрываясь от реальности. Представим, что у нас есть следующий интерфейс:&lt;br /&gt;
&amp;lt;Vip&amp;gt;interface покупатель&lt;br /&gt;
properties&lt;br /&gt;
    зарплата : unsigned(o).&lt;br /&gt;
    фильтрМашин : () -&amp;gt; list::filter.&lt;br /&gt;
end interface покупатель&amp;lt;/Vip&amp;gt;&lt;br /&gt;
Попробуем написать реализацию&lt;br /&gt;
&amp;lt;Vip&amp;gt;implement покупатель&lt;br /&gt;
facts&lt;br /&gt;
    зарплата : unsigned.&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрМашин() = &lt;br /&gt;
&lt;br /&gt;
{(Авто) :- Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей)&lt;br /&gt;
implement покупатель&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Детали реализации===&lt;br /&gt;
===Где использовать===&lt;br /&gt;
&lt;br /&gt;
Это не оконченная статья&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1288</id>
		<title>Анонимные предикаты</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1288"/>
		<updated>2007-11-16T05:30:11Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Анонимные предикаты - новая возможность компилятора, которая будет реализована начиная с версии 7.2&lt;br /&gt;
 &lt;br /&gt;
===Вступление===&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Vip&amp;gt;&lt;br /&gt;
interface авто&lt;br /&gt;
properties&lt;br /&gt;
    цена : unsigned(o).&lt;br /&gt;
end interface авто&lt;br /&gt;
&lt;br /&gt;
class выборАвтомобиля&lt;br /&gt;
predicates&lt;br /&gt;
    неДорогие : () -&amp;gt; авто* Результат.&lt;br /&gt;
...&lt;br /&gt;
implement выборАвтомобиля&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter({(Авто) :- Авто:цена &amp;lt;= 10000, !}, списокАвтомобилей).&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Казалось бы ничего нового, ведь то же самое можно записать при помощи дополнительного предиката. Например так&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    фильтрДляНедорогих : (авто Авто).&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрДляНедорогих(Авто) :- Авто:цена &amp;lt;= 10000, !.&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter(фильтрДляНедорогих, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но сегодня для нас сумма 10000 является пределом мечтаний, а завтра 12000, как быть? Есть два решения, или менять константу, или добавить факт-переменную. Анонимный предикат позволит сделать это изящней.&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    неДорогие2 : (unsigned НашиВозможности).&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие2(НашиВозможности) = list::filter({(Авто) :- Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если внимательно присмотреться, то тут мы наблюдаем картину, ранее недоступную невооружённым глазом. Переменная &amp;#039;&amp;#039;НашиВозможности&amp;#039;&amp;#039; из предиката &amp;#039;&amp;#039;&amp;#039;&amp;#039;неДорогие2&amp;#039;&amp;#039;&amp;#039;&amp;#039; перекочевала во внутрь другого предиката - фильтра! Вот это уже нельзя сделать использую дополнительный предикат, как раньше.&lt;br /&gt;
&lt;br /&gt;
===Погружение===&lt;br /&gt;
Давайте еще усложним наш пример, не сильно отрываясь от реальности. Представим, что у нас есть следующий интерфейс:&lt;br /&gt;
&amp;lt;Vip&amp;gt;interface покупатель&lt;br /&gt;
properties&lt;br /&gt;
    зарплата : unsigned(o).&lt;br /&gt;
    фильтрМашин : () -&amp;gt; list::filter.&lt;br /&gt;
end interface покупатель&amp;lt;/Vip&amp;gt;&lt;br /&gt;
Попробуем написать реализацию&lt;br /&gt;
&amp;lt;Vip&amp;gt;implement покупатель&lt;br /&gt;
facts&lt;br /&gt;
    зарплата : unsigned.&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрМашин() = &lt;br /&gt;
implement покупатель&amp;lt;/Vip&amp;gt;&lt;br /&gt;
===Детали реализации===&lt;br /&gt;
===Где использовать===&lt;br /&gt;
&lt;br /&gt;
Это не оконченная статья&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1287</id>
		<title>Анонимные предикаты</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1287"/>
		<updated>2007-11-16T05:29:18Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Анонимные предикаты - новая возможность компилятора, которая будет реализована начиная с версии 7.2&lt;br /&gt;
 &lt;br /&gt;
===Вступление===&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Vip&amp;gt;&lt;br /&gt;
interface авто&lt;br /&gt;
properties&lt;br /&gt;
    цена : unsigned(o).&lt;br /&gt;
end interface авто&lt;br /&gt;
&lt;br /&gt;
class выборАвтомобиля&lt;br /&gt;
predicates&lt;br /&gt;
    неДорогие : () -&amp;gt; авто* Результат.&lt;br /&gt;
...&lt;br /&gt;
implement выборАвтомобиля&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter({(Авто) :- Авто:цена &amp;lt;= 10000, !}, списокАвтомобилей).&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Казалось бы ничего нового, ведь то же самое можно записать при помощи дополнительного предиката. Например так&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    фильтрДляНедорогих : (авто Авто).&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрДляНедорогих(Авто) :- Авто:цена &amp;lt;= 10000, !.&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter(фильтрДляНедорогих, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но сегодня для нас сумма 10000 является пределом мечтаний, а завтра 12000, как быть? Есть два решения, или менять константу, или добавить факт-переменную. Анонимный предикат позволит сделать это изящней.&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    неДорогие2 : (unsigned НашиВозможности).&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие2(НашиВозможности) = list::filter({(Авто) :- Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если внимательно присмотреться, то тут мы наблюдаем картину, ранее недоступную невооружённым глазом. Переменная &amp;#039;&amp;#039;НашиВозможности&amp;#039;&amp;#039; из предиката &amp;#039;&amp;#039;&amp;#039;&amp;#039;неДорогие2&amp;#039;&amp;#039;&amp;#039;&amp;#039; перекочевала во внутрь другого предиката - фильтра! Вот это уже нельзя сделать использую дополнительный предикат, как раньше.&lt;br /&gt;
&lt;br /&gt;
===Погружение===&lt;br /&gt;
Давайте еще усложним наш пример, не сильно отрываясь от реальности. Представим, что у нас есть следующий интерфейс:&lt;br /&gt;
&amp;lt;Vip&amp;gt;interface покупатель&lt;br /&gt;
properties&lt;br /&gt;
    зарплата : unsigned(o).&lt;br /&gt;
    фильтрМашин : () -&amp;gt; list::filter.&lt;br /&gt;
end interface покупатель&amp;lt;/Vip&amp;gt;&lt;br /&gt;
Попробуем написать реализацию&lt;br /&gt;
&amp;lt;Vip&amp;gt;implement покупатель&lt;br /&gt;
facts&lt;br /&gt;
    зарплата : unsigned.&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрМашин() = &lt;br /&gt;
implement покупатель&amp;lt;/Vip&amp;gt;&lt;br /&gt;
===Детали реализации===&lt;br /&gt;
===Где использоватьь===&lt;br /&gt;
&lt;br /&gt;
Это не оконченная статья&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B_Visual_Prolog&amp;diff=1256</id>
		<title>Основы Системы Visual Prolog</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B_Visual_Prolog&amp;diff=1256"/>
		<updated>2007-11-15T06:53:39Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{FundamentalVisualPrologNavbar}}&lt;br /&gt;
&lt;br /&gt;
В этом руководстве мы представляем программу, разработанную на платформе системы программирования Visual Prolog. Алгоритмы, используемые в руководстве , - те же, что и в руководстве &amp;quot;Fundamental Prolog&amp;quot; (Часть 2).&lt;br /&gt;
&lt;br /&gt;
==Отличия между Visual Prolog и традиционным Прологом==&lt;br /&gt;
&lt;br /&gt;
Различия между традиционным Прологом и Visual Prolog можно провести по следующим категориям:&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;Различия в структуре программы&amp;#039;&amp;#039;&amp;#039;:&amp;lt;br/&amp;gt;Различия между Visual Prolog и традиционным Прологом имеются, но они не существенны. Они сводятся к пониманию того, как различаются декларации (declarations) от определений (definitions), а также к явному выделению цели &amp;#039;&amp;#039;Goal&amp;#039;&amp;#039;. Все это поддержано специальными ключевыми словами.&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;Файловая структура программы:&amp;#039;&amp;#039;&amp;#039;:&amp;lt;br/&amp;gt;Visual Prolog предоставляет возможности структуризации программ с использованием файлов различного типа.&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;Границы видимости:&amp;#039;&amp;#039;&amp;#039;&amp;lt;br/&amp;gt;Программа системы Visual Prolog может поддерживать функционирование, располагающееся в различных модулях с использованием концепции &amp;#039;&amp;#039;идентификация пространств&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;Объектная ориентированность:&amp;#039;&amp;#039;&amp;#039;&amp;lt;br/&amp;gt;Программа на языке Visual Prolog может быть написана как объектно ориентированная программа с использованием классических свойств объектно-ориентированной парадигмы.&lt;br /&gt;
&lt;br /&gt;
===Program Structure Differences===&lt;br /&gt;
&lt;br /&gt;
====Declarations and Definitions====&lt;br /&gt;
&lt;br /&gt;
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&amp;#039;s clause was directly written down using the traditional Prolog&amp;#039;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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
By &amp;#039;&amp;#039;&amp;quot;runtime exceptions&amp;quot;&amp;#039;&amp;#039;, 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 &amp;#039;&amp;#039;runtime&amp;#039;&amp;#039; error (in programs written using most other compilers, but not Visual Prolog) and the program would fail there.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;runtime exception&amp;#039;&amp;#039; 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!&lt;br /&gt;
&lt;br /&gt;
All this automatically implies that the compiler &amp;#039;&amp;#039;&amp;#039;has&amp;#039;&amp;#039;&amp;#039; to be given explicit instructions regarding the predicates and domains that exist in the code using appropriate declarations before the same are defined.&lt;br /&gt;
&lt;br /&gt;
====Keywords====&lt;br /&gt;
&lt;br /&gt;
A Visual Prolog program consists of Prolog code which is punctuated into different &amp;#039;&amp;#039;&amp;#039;sections&amp;#039;&amp;#039;&amp;#039; by appropriate keywords that inform the compiler the code it has to generate. For example, there are keywords that differentiate the &amp;#039;&amp;#039;declarations&amp;#039;&amp;#039; from the &amp;#039;&amp;#039;definitions&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
The exception to this rule, are the keywords &amp;quot;implement&amp;quot; and &amp;quot;end implement&amp;quot; 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 &amp;quot;class&amp;quot; can, for now (i.e. in this tutorial), think of it as a module or a section of the overall program code.&lt;br /&gt;
&lt;br /&gt;
For the purpose of this tutorial, we&amp;#039;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.&lt;br /&gt;
&lt;br /&gt;
The list of the keywords that you need to know in this tutorial is the following:&lt;br /&gt;
&lt;br /&gt;
implement and end implement&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
open&lt;br /&gt;
&lt;br /&gt;
This keyword is used to extend the &amp;#039;&amp;#039;scope visibility&amp;#039;&amp;#039; of the class. It is to be used just after the implement keyword.&lt;br /&gt;
&lt;br /&gt;
constants&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;PDC Prolog&amp;quot; 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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;constants&lt;br /&gt;
pdc=&amp;quot;PDC Prolog&amp;quot;.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
domains&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
class facts&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;definitions&amp;#039;&amp;#039; of the previously declared predicates. And you would find that the predicates used here would follow the syntax as declared in the &amp;#039;&amp;#039;class predicates&amp;#039;&amp;#039; section.&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
&lt;br /&gt;
This section defines the main entry point into a Visual Prolog program. A more detailed explanation is given below.&lt;br /&gt;
&lt;br /&gt;
====Goal====&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;compiler&amp;#039;&amp;#039; 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 &amp;#039;&amp;#039;compiler&amp;#039;&amp;#039; is doing its work. Hence, the &amp;#039;&amp;#039;compiler&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
In order to do that, there is a special section indicated by the keyword &amp;#039;&amp;#039;Goal&amp;#039;&amp;#039;. 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.&lt;br /&gt;
&lt;br /&gt;
===File Considerations===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
===Scope Access Issues===&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;class&amp;#039;&amp;#039; loosely as being synonymous to &amp;#039;&amp;#039;modules&amp;#039;&amp;#039;. Normally, Visual Prolog defines each class in its own separate file.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
    pred3:-&lt;br /&gt;
        ...&lt;br /&gt;
        !.&lt;br /&gt;
&lt;br /&gt;
    pred2:-&lt;br /&gt;
        class1::pred1, %pred1 is not known in this file. It is defined in some other file, hence a class qualifier is needed&lt;br /&gt;
        pred3,&lt;br /&gt;
        ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;&amp;#039;::&amp;#039;&amp;#039;&amp;#039; (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.&lt;br /&gt;
&lt;br /&gt;
This behavior is technically explained thus: The &amp;#039;&amp;#039;scope visibility&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;-&amp;#039;&amp;#039; end implement keywords). The predicates defined therein can call each other without the class qualifier and the double colon (::) token preceding it.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;open class1&lt;br /&gt;
            ...&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    pred3:-&lt;br /&gt;
        ...&lt;br /&gt;
        !.&lt;br /&gt;
&lt;br /&gt;
    pred2:-&lt;br /&gt;
        pred1, %Note: &amp;quot;class1::&amp;quot; qualifier is not needed anymore, as the scope area is extended using the &amp;#039;open&amp;#039; keyword&lt;br /&gt;
        pred3,&lt;br /&gt;
        ...&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Object Orientation===&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;family1&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
==A Full Fledged Example: family1.prj6==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Download:&amp;#039;&amp;#039;&amp;#039; Source files of the example project used in this tutorial.&amp;lt;img alt src=&amp;quot;../tut11/images/tut11_dot.gif&amp;quot; border=&amp;quot;0&amp;quot; width=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us now put all the knowledge we&amp;#039;ve gathered together to create our first Visual Prolog program. This will contain the same basic logic that was explored in the Tutorial &amp;quot;Fundamental Prolog. Part 2&amp;quot;. All the code that is to be written for this tutorial is shown below. It has to be written into the file called &amp;#039;&amp;#039;family1.pro&amp;#039;&amp;#039;. 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.&lt;br /&gt;
&lt;br /&gt;
But first of all, let us acquaint ourselves with the main code of the program:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;implement family1&lt;br /&gt;
    open core&lt;br /&gt;
&lt;br /&gt;
constants&lt;br /&gt;
    className = &amp;quot;family1&amp;quot;.&lt;br /&gt;
        classVersion = &amp;quot;$JustDate: $$Revision: $&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    classInfo(className, classVersion).&lt;br /&gt;
&lt;br /&gt;
domains&lt;br /&gt;
    gender = female(); male().&lt;br /&gt;
&lt;br /&gt;
class facts - familyDB&lt;br /&gt;
    person : (string Name, gender Gender).&lt;br /&gt;
    parent : (string Person, string Parent).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    father : (string Person, string Father) nondeterm anyflow.&lt;br /&gt;
clauses&lt;br /&gt;
    father(Person, Father) :-&lt;br /&gt;
        parent(Person, Father),&lt;br /&gt;
        person(Father, male()).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    grandFather : (string Person, string GrandFather) nondeterm anyflow.&lt;br /&gt;
clauses&lt;br /&gt;
    grandFather(Person, GrandFather) :-&lt;br /&gt;
        parent(Person, Parent),&lt;br /&gt;
        father(Parent, GrandFather).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    ancestor : (string Person, string Ancestor) nondeterm anyflow.&lt;br /&gt;
clauses&lt;br /&gt;
    ancestor(Person, Ancestor) :-&lt;br /&gt;
        parent(Person, Ancestor).&lt;br /&gt;
    ancestor(Person, Ancestor) :-&lt;br /&gt;
        parent(Person, P1),&lt;br /&gt;
        ancestor(P1, Ancestor).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    reconsult : (string FileName).&lt;br /&gt;
clauses&lt;br /&gt;
    reconsult(FileName) :-&lt;br /&gt;
        retractFactDB( familyDB),&lt;br /&gt;
        file::consult(FileName, familyDB).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    run():-&lt;br /&gt;
        console::init(),&lt;br /&gt;
        stdIO::write(&amp;quot;Load data\n&amp;quot;),&lt;br /&gt;
        reconsult(&amp;quot;fa.txt&amp;quot;),&lt;br /&gt;
        stdIO::write(&amp;quot;\nfather test\n&amp;quot;),&lt;br /&gt;
        father(X, Y),&lt;br /&gt;
            stdIO::writef(&amp;quot;% is the father of %\n&amp;quot;, Y, X),&lt;br /&gt;
        fail.&lt;br /&gt;
    run():-&lt;br /&gt;
        stdIO::write(&amp;quot;\ngrandFather test\n&amp;quot;),&lt;br /&gt;
        grandFather(X, Y),&lt;br /&gt;
            stdIO::writef(&amp;quot;% is the grandfather of %\n&amp;quot;, Y, X),&lt;br /&gt;
        fail.&lt;br /&gt;
    run():-&lt;br /&gt;
        stdIO::write(&amp;quot;\nancestor of Pam test\n&amp;quot;),&lt;br /&gt;
        X = &amp;quot;Pam&amp;quot;,&lt;br /&gt;
        ancestor(X, Y),&lt;br /&gt;
            stdIO::writef(&amp;quot;% is the ancestor of %\n&amp;quot;, Y, X),&lt;br /&gt;
        fail.&lt;br /&gt;
    run():-&lt;br /&gt;
        stdIO::write(&amp;quot;End of test\n&amp;quot;).&lt;br /&gt;
end implement family1&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
mainExe::run(family1::run).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Step 1: Create a New Console Project in the IDE===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1a.&amp;#039;&amp;#039;&amp;#039; After starting the Visual Prolog IDE, click on the New menu item from the Project menu.&lt;br /&gt;
&lt;br /&gt;
[[Image:FundamentalVisualProlog1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1b.&amp;#039;&amp;#039;&amp;#039; A dialog would be presented to you. Enter all the relevant information. Ensure that the UI Strategy is &amp;#039;&amp;#039;Console&amp;#039;&amp;#039; and NOT &amp;#039;&amp;#039;GUI&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:FundamentalVisualProlog2.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Step 2: Build an Empty Project===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2a.&amp;#039;&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2b.&amp;#039;&amp;#039;&amp;#039; 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)&lt;br /&gt;
&lt;br /&gt;
While building the project, take a look at the messages that are dynamically displayed in the &amp;#039;&amp;#039;&amp;#039;Messages&amp;#039;&amp;#039;&amp;#039; window of the IDE. (In the case the &amp;#039;&amp;#039;&amp;#039;Messages&amp;#039;&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
===Step 3: Populate the family1.pro with the Actual Code===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3a.&amp;#039;&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
[[Image:FundamentalVisualProlog5.jpg]]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3b.&amp;#039;&amp;#039;&amp;#039; After you perform the copy-paste operation, the family1.pro window would now look as shown below:&lt;br /&gt;
&lt;br /&gt;
[[Image:FundamentalVisualProlog6.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Step 4: Rebuild the Code===&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;ALL&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
During the &amp;#039;&amp;#039;Build&amp;#039;&amp;#039; 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&amp;#039;ll notice the IDE was forced to build the project &amp;#039;&amp;#039;twice&amp;#039;&amp;#039; because of some additional include statements.&lt;br /&gt;
&lt;br /&gt;
[[Image:FundamentalVisualProlog7.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Step 5: Execute the Program===&lt;br /&gt;
&lt;br /&gt;
We now have our first true application complied using Visual Prolog. In order to test the application we&amp;#039;ve just compiled, you can run the program using the Build | Run &amp;#039;&amp;#039;in&amp;#039;&amp;#039; 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&amp;#039;ll come to the syntax of that file a bit later.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;fa.txt&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
The contents of the file would be as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
    person(&amp;quot;Judith&amp;quot;,female()).&lt;br /&gt;
        person(&amp;quot;Bill&amp;quot;,male()).&lt;br /&gt;
        person(&amp;quot;John&amp;quot;,male()).&lt;br /&gt;
        person(&amp;quot;Pam&amp;quot;,female()).&lt;br /&gt;
        parent(&amp;quot;John&amp;quot;,&amp;quot;Judith&amp;quot;).&lt;br /&gt;
        parent(&amp;quot;Bill&amp;quot;,&amp;quot;John&amp;quot;).&lt;br /&gt;
        parent(&amp;quot;Pam&amp;quot;,&amp;quot;Bill&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;dynamic&amp;#039;&amp;#039; 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.&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;person&amp;#039;&amp;#039; 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).&lt;br /&gt;
&lt;br /&gt;
Now, when you run the program using the Build | Run in the Window menu command, this is what you&amp;#039;ll see:&lt;br /&gt;
&lt;br /&gt;
[[Image:FundamentalVisualProlog8.jpg]]&lt;br /&gt;
&lt;br /&gt;
The program processes the information in the file fa.txt and works out the logical sequence of the parentages of people given there.&lt;br /&gt;
&lt;br /&gt;
===A Round up of the Program===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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 &amp;#039;&amp;#039;backtracking&amp;#039;&amp;#039;, because the Prolog engine literally seems to backtrack over code which had earlier executed.&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;fathers&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
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&amp;#039;s name (in case the name of the offspring was bound) OR the offspring&amp;#039;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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
    run():-&lt;br /&gt;
        console::init(),&lt;br /&gt;
        stdIO::write(&amp;quot;Load  data\n&amp;quot;),&lt;br /&gt;
        reconsult(&amp;quot;fa.txt&amp;quot;),&lt;br /&gt;
        stdIO::write(&amp;quot;\nfather test\n&amp;quot;),&lt;br /&gt;
        father(X,Y),&lt;br /&gt;
        stdIO::writef(&amp;quot;% is the father of %\n&amp;quot;, Y, X),&lt;br /&gt;
        fail.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
&lt;br /&gt;
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&amp;#039;s object-oriented features. A complete console-based application (family1) was developed in this tutorial, which explains, how to create such a program.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipРуководства]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=Turbo_Prolog&amp;diff=1167</id>
		<title>Turbo Prolog</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=Turbo_Prolog&amp;diff=1167"/>
		<updated>2007-11-10T19:02:13Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Turbo Prolog распространялся на рынке в 80-е годы компанией Borland. Turbo Prolog был разработан учредителями PDC.&lt;br /&gt;
&lt;br /&gt;
==Примеры кодов Turbo Prolog / программы, доступные в Интернете==&lt;br /&gt;
&lt;br /&gt;
Тексты программ на Turbo Prolog с небольшими изменениями могут быть &amp;quot;переведены&amp;quot; на Visual Prolog. Поэтому старые примеры системы Turbo Prolog которые можно найти в Интернете, достойны изучения.&lt;br /&gt;
&lt;br /&gt;
[http://perso.orange.fr/colin.barker/tpro1/tpro.htm Colin Barker*s Turbo Prolog Goodies]&lt;br /&gt;
&lt;br /&gt;
Несколько примеров на Turbo Prolog разработал Frank Bergmann:&lt;br /&gt;
*[http://www.fraber.de/games/chess/index.html Chess]&lt;br /&gt;
*[http://www.fraber.de/games/queens/index.html Queens]&lt;br /&gt;
*[http://www.fraber.de/games/world/index.html Tarskies World]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1089</id>
		<title>Анонимные предикаты</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1089"/>
		<updated>2007-11-08T05:30:30Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Анонимные предикаты - новая возможность компилятора, которая будет реализована начиная с версии 7.2&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Vip&amp;gt;&lt;br /&gt;
interface авто&lt;br /&gt;
properties&lt;br /&gt;
    цена : unsigned(o).&lt;br /&gt;
end interface авто&lt;br /&gt;
&lt;br /&gt;
class выборАвтомобиля&lt;br /&gt;
predicates&lt;br /&gt;
    неДорогие : () -&amp;gt; авто* Результат.&lt;br /&gt;
...&lt;br /&gt;
implement выборАвтомобиля&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter({(Авто) :- Авто:цена &amp;lt;= 10000, !}, списокАвтомобилей).&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Казалось бы ничего нового, ведь то же самое можно записать при помощи дополнительного предиката. Например так&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    фильтрДляНедорогих : (авто Авто).&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрДляНедорогих(Авто) :- Авто:цена &amp;lt;= 10000, !.&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter(фильтрДляНедорогих, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но сегодня для нас сумма 10000 является пределом мечтаний, а завтра 12000, как быть? Есть два решения, или менять константу, или добавить факт-переменную. Анонимный предикат позволит сделать это изящней.&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    неДорогие2 : (unsigned НашиВозможности).&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие2(НашиВозможности) = list::filter({(Авто) :- Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если внимательно присмотреться, то тут мы наблюдаем картину, ранее недоступную невооружённым глазом. Переменная &amp;#039;&amp;#039;НашиВозможности&amp;#039;&amp;#039; из предиката &amp;#039;&amp;#039;&amp;#039;&amp;#039;неДорогие2&amp;#039;&amp;#039;&amp;#039;&amp;#039; перекочевала во внутрь другого предиката - фильтра! Вот это уже нельзя сделать использую дополнительный предикат, как раньше.&lt;br /&gt;
&lt;br /&gt;
Это не оконченная статья&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1088</id>
		<title>Анонимные предикаты</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1088"/>
		<updated>2007-11-07T19:44:17Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Анонимные предикаты - новая возможность компилятора, которая будет реализована начиная с версии 7.2&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Vip&amp;gt;&lt;br /&gt;
interface авто&lt;br /&gt;
properties&lt;br /&gt;
    цена : unsigned(o).&lt;br /&gt;
end interface авто&lt;br /&gt;
&lt;br /&gt;
class выборАвтомобиля&lt;br /&gt;
predicates&lt;br /&gt;
    недорогие : () -&amp;gt; авто* Результат.&lt;br /&gt;
...&lt;br /&gt;
implement выборАвтомобиля&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter({(Авто):-Авто:цена &amp;lt;= 10000, !}, списокАвтомобилей).&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Казалось бы ничего нового, ведь то же самое можно записать при помощи дополнительного предиката. Например так&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    фильтрДляНедорогих : (авто Авто).&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрДляНедорогих(Авто):- Авто:цена &amp;lt;= 10000, !.&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter(фильтрДляНедорогих, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но сегодня для нас сумма 10000 является пределом мечтаний, а завтра 12000, как быть? Есть два решения, или менять константу, или добавить факт-переменную. Анонимный предикат позволит сделать это изящней.&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    неДорогие2 : (unsigned НашиВозможности).&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие2(НашиВозможности) = list::filter({(Авто):-Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если внимательно присмотреться, то тут мы наблюдаем картину, ранее недоступную невооружённым глазом. Переменная &amp;#039;&amp;#039;НашиВозможности&amp;#039;&amp;#039; из предиката &amp;#039;&amp;#039;&amp;#039;&amp;#039;неДорогие2&amp;#039;&amp;#039;&amp;#039;&amp;#039; перекочевала во внутрь другого предиката - фильтра! Вот это уже нельзя сделать использую дополнительный предикат, как раньше.&lt;br /&gt;
&lt;br /&gt;
Это не оконченная статья&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1087</id>
		<title>Анонимные предикаты</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1087"/>
		<updated>2007-11-07T19:43:36Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Анонимные предикаты - новая возможность компилятора, которая будет реализована начиная с версии 7.2&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Vip&amp;gt;&lt;br /&gt;
interface авто&lt;br /&gt;
properties&lt;br /&gt;
    цена : unsigned(o).&lt;br /&gt;
end interface авто&lt;br /&gt;
&lt;br /&gt;
class выборАвтомобиля&lt;br /&gt;
predicates&lt;br /&gt;
    недорогие : () -&amp;gt; авто* Результат.&lt;br /&gt;
...&lt;br /&gt;
implement выборАвтомобиля&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter({(Авто):-Авто:цена &amp;lt;= 10000, !}, списокАвтомобилей).&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Казалось бы ничего нового, ведь то же самое можно записать при помощи дополнительного предиката. Например так&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    фильтрДляНедорогих : (авто Авто).&lt;br /&gt;
clauses&lt;br /&gt;
    фильтрДляНедорогих(Авто):- Авто:цена &amp;lt; 10000, !.&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter(фильтрДляНедорогих, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но сегодня для нас сумма 10000 является пределом мечтаний, а завтра 12000, как быть? Есть два решения, или менять константу, или добавить факт-переменную. Анонимный предикат позволит сделать это изящней.&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    неДорогие2 : (unsigned НашиВозможности).&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие2(НашиВозможности) = list::filter({(Авто):-Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если внимательно присмотреться, то тут мы наблюдаем картину, ранее недоступную невооружённым глазом. Переменная &amp;#039;&amp;#039;НашиВозможности&amp;#039;&amp;#039; из предиката &amp;#039;&amp;#039;&amp;#039;&amp;#039;неДорогие2&amp;#039;&amp;#039;&amp;#039;&amp;#039; перекочевала во внутрь другого предиката - фильтра! Вот это уже нельзя сделать использую дополнительный предикат, как раньше.&lt;br /&gt;
&lt;br /&gt;
Это не оконченная статья&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=Visual_Prolog_%D0%B4%D0%BB%D1%8F_%D1%87%D0%B0%D0%B9%D0%BD%D0%B8%D0%BA%D0%BE%D0%B2&amp;diff=1080</id>
		<title>Visual Prolog для чайников</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=Visual_Prolog_%D0%B4%D0%BB%D1%8F_%D1%87%D0%B0%D0%B9%D0%BD%D0%B8%D0%BA%D0%BE%D0%B2&amp;diff=1080"/>
		<updated>2007-11-07T16:21:31Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;#039;&amp;#039;Visual Prolog для Чайников&amp;#039;&amp;#039; (в оригинале Visual Prolog For Tyros) пока является наиболее подробной книгой по Visual Prolog, написанная Eduardo Costa.&lt;br /&gt;
&lt;br /&gt;
Мы представляем здесь макет перевода, который вместе с форматированием выполнил Марк Сафронов.&lt;br /&gt;
Переводчиком учтён ряд замечаний, сделанных участниками русскоязычного [http://www.progz.ru/forum/index.php?showforum=10 Форума по Прологу] &lt;br /&gt;
&lt;br /&gt;
Перевод нельзя считать законченным в силу терминологических проблем, с которыми встретился переводчик.&lt;br /&gt;
&lt;br /&gt;
===Содержание===&lt;br /&gt;
&lt;br /&gt;
*I Savoir-faire&lt;br /&gt;
**Введение&lt;br /&gt;
**Формы&lt;br /&gt;
**События мыши&lt;br /&gt;
**Некоторые изображения&lt;br /&gt;
**Хороновские Клаузы&lt;br /&gt;
**Консольные приложения&lt;br /&gt;
**Грамматики&lt;br /&gt;
**Рисование&lt;br /&gt;
**Типы данных&lt;br /&gt;
**Как это решается на Прологе&lt;br /&gt;
**Факты&lt;br /&gt;
**Классы и Объекты&lt;br /&gt;
**Джузеппе Пеано (Giuseppe Peano)&lt;br /&gt;
**L-Системы&lt;br /&gt;
**Игры&lt;br /&gt;
**Анимация&lt;br /&gt;
**Текстовый редактор&lt;br /&gt;
**Печать&lt;br /&gt;
**Фомы с закладками и прочие прелести&lt;br /&gt;
**Ошибки&lt;br /&gt;
**Менеджер баз данных&lt;br /&gt;
**Книги и статьи&lt;br /&gt;
*II Искусственный интеллект&lt;br /&gt;
**Поиск&lt;br /&gt;
**Нейронные сети&lt;br /&gt;
**Alpha Beta Pruning&lt;br /&gt;
&lt;br /&gt;
===Скачать===&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_8.pdf Visual Prolog for Tyros - оригинал] ( Язык:En. PDF формат, 252 страницы).&lt;br /&gt;
**[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus_pdf.zip Язык:Ru; Zip(PDF)  формат, 4.05 Mb]&lt;br /&gt;
**[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus_docx.zip Язык:Ru; Zip(DOCX)  формат 5.14 Mb]&lt;br /&gt;
**[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus_doc.zip Язык:Ru; Zip(DOC)  формат, 10.5 Mb]&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Перевод (незавершенный)&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus.doc Язык:Ru; DOC формат 39.9 Mb].&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus_doc.zip Язык:Ru; Zip(DOC)  формат, 10.5 Mb]&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus.docx Язык:Ru; DOCX  формат, 5.21 Mb]&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus_docx.zip Язык:Ru; Zip(DOCX)  формат 5.14 Mb]&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus.pdf Язык:Ru; PDF формат, 4.52 Mb]&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_rus_pdf.zip Язык:Ru; Zip(PDF)  формат, 4.05 Mb]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Примеры&lt;br /&gt;
*[http://www.visual-prolog.com/vip/tutorial/tut14/tyros71_8.zip Примеры к руководству] (ZIP формат)&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
[[en:Visual Prolog for Tyros]]&lt;br /&gt;
[[Category:VipРуководства]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1023</id>
		<title>Анонимные предикаты</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D1%8B&amp;diff=1023"/>
		<updated>2007-11-06T09:43:57Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Анонимные предикаты - новая возможность компилятора, которая будет реализована начиная с версии 7.2&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Vip&amp;gt;&lt;br /&gt;
interface авто&lt;br /&gt;
properties&lt;br /&gt;
    цена : unsigned(o).&lt;br /&gt;
end interface авто&lt;br /&gt;
&lt;br /&gt;
class выборАвтомобиля&lt;br /&gt;
predicates&lt;br /&gt;
    недорогие : () -&amp;gt; авто* Результат.&lt;br /&gt;
...&lt;br /&gt;
implement выборАвтомобиля&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter({(Авто):-Авто:цена &amp;lt;= 10000, !}, списокАвтомобилей).&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Казалось бы ничего нового, ведь то же самое можно записать при помощи дополнительного предиката. Например так&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    фильтерДляНедорогих : (авто Авто).&lt;br /&gt;
clauses&lt;br /&gt;
    фильтерДляНедорогих(Авто):- Авто:цена &amp;lt; 10000, !.&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие() = list::filter(фильтерДляНедорогих, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но сегодня для нас сумма 10000 является пределом мечтаний, а завтра 12000, как быть? Есть два решения, или менять константу, или добавить факт-переменную. Анонимный предикат позволит сделать это изящней.&lt;br /&gt;
&amp;lt;Vip&amp;gt;predicates&lt;br /&gt;
    неДорогие2 : (unsigned НашиВозможности).&lt;br /&gt;
clauses&lt;br /&gt;
    неДорогие2(НашиВозможности) = list::filter({(Авто):-Авто:цена &amp;lt;= НашиВозможности, !}, списокАвтомобилей).&amp;lt;/Vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если внимательно присмотреться, то тут мы наблюдаем картину, ранее недоступную невооружённым глазом. Переменная &amp;#039;&amp;#039;НашиВозможности&amp;#039;&amp;#039; из предиката &amp;#039;&amp;#039;&amp;#039;&amp;#039;неДорогие2&amp;#039;&amp;#039;&amp;#039;&amp;#039; перекочевала во внутрь другого предиката - фильтра! Вот это уже нельзя сделать использую дополнительный предикат, как раньше.&lt;br /&gt;
&lt;br /&gt;
Это не оконченная статья&lt;br /&gt;
&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D1%81%D0%BE%D0%B1%D0%B5%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F_append&amp;diff=1021</id>
		<title>Особенности применения append</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%9E%D1%81%D0%BE%D0%B1%D0%B5%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F_append&amp;diff=1021"/>
		<updated>2007-11-06T07:20:34Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;автор: Thomas Linder Puls. PDC&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Соединение списков в Прологе затратно по своей природе. Эта проблема связана с «персистентной» природой прологовских списков. Структура персистентных данных неизменяема и неуничтожима при манипулировании этими данными.&lt;br /&gt;
&lt;br /&gt;
Например, если я соединяю два списка, то в результате они продолжают существовать. Способ создания объединённого списка без разрушения исходных списков, состоит в копировании первого списка в начало второго. Так, соединяя L1 и L2, мы повторно используем L2 в качестве хвоста нового списка, а L1 копируется в начало нового списка. Сами элементы не копируются, копируются только ячейки списка. Поэтому затраты на соединение пропорциональны длине первого списка.&lt;br /&gt;
&lt;br /&gt;
Используя предикат &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039; нужно быть очень осторожным, очень легко изменить линейную сложность алгоритма (О(N)) на квадратичную (О(N*N)).&lt;br /&gt;
Рассмотрим пример. Имеем бинарное дерево:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
domains &lt;br /&gt;
  binTree = &lt;br /&gt;
    empty(); &lt;br /&gt;
    tree(binTree Left, integer Node, binTree Right).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Мы хотим создать список всех элементов дерева. Вот очевидный способ решения этой задачи, используя предикат &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
clauses &lt;br /&gt;
  elems(empty()) = []. &lt;br /&gt;
  elems(tree(Left, Node, Right)) = Elems :- &lt;br /&gt;
    LeftElems = elems(Left), &lt;br /&gt;
    RightElems = elems(Right), &lt;br /&gt;
    Elems = append(LeftElems, [Node|RightElems]).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Т.е. мы находим элементы в левом поддереве, затем в правом поддереве и соединяем их, вставив узел перед элементами правого поддерева. &lt;br /&gt;
&lt;br /&gt;
Это совершенно очевидный алгоритм, но очень плохой. Проблемой является левое поддерево: сначала мы создаём список всех его элементов, затем соединяем, копируя этот список перед оставшимися элементами. В наихудшем случае все дерево представляет собой простой путь вниз.&lt;br /&gt;
&lt;br /&gt;
Если в дереве N узлов, то мы имеем одну вершину и N-1 элементов в левом поддереве и пустое правое поддерево. В этом уровне, &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039; выполнит N-1 операцию. На следующем уровне, по аналогии, будут выполнены N-2 операций, т.д. По моим представлениям о высшей математике, всего получится N*N/2 операций. Поэтому, в наихудшем случае, этот алгоритм имеет квадратичную сложность. Правда, в лучшем случае, алгоритм линейный. Оставляю вам доказательство этого утверждения.&lt;br /&gt;
&lt;br /&gt;
Лично я никогда не применяю &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039; в рекурсивном алгоритме. Я использую его, например, для соединения содержимого двух списковых элементов управления. Вообще, я практически никогда не использую &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039;. Когда я его вижу, это вызывает настороженность - нет ли проблемы с этим алгоритмом?&lt;br /&gt;
&lt;br /&gt;
К счастью, в нашем случае легко избежать использования &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039;. Имеется стандартная «хитрость», которая помогает решить проблему без &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039; с линейной сложностью в худшем случае.&lt;br /&gt;
Введём вспомогательный предикат, который не только собирает узлы, но и объединяет их в список. Звучит это ещё более сложно, чем раньше, но в действительности это не так. Вот предлагаемый предикат:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
clauses &lt;br /&gt;
  elems_append(empty(), L) = L. &lt;br /&gt;
  elems_append(tree(Left, Node, Right), L) = M :- &lt;br /&gt;
    RightL = elems_append(Right, L), &lt;br /&gt;
    M = elems_append(Left, [Node|RightL]).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Если дерево пусто, то просто возвращается исходный список. Если дерево не пусто, то сначала присоединяем элементы правого дерева к L, затем помещаем узел в начало и, наконец, присоединяем элементы левого дерева. В результате процесс идёт с конца в начало. Этот алгоритм не только не использует &amp;#039;&amp;#039;&amp;#039;append&amp;#039;&amp;#039;&amp;#039;, но даже в худшем случае имеет сложность О(N). Оставляю вам доказательство этого.&lt;br /&gt;
&lt;br /&gt;
Этот предикат не решает исходной проблемы, но следующий предикат делает это:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
clauses &lt;br /&gt;
  elem(Tree) = elem_append(Tree, []).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Т.е. присоединяет пустой список к элементам дерева.&lt;br /&gt;
[[Категория:VipLanguage]]&lt;br /&gt;
[[Категория:VipКомментарии]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%94%D0%BE%D0%BC%D0%B5%D0%BD%D1%8B_%D0%B8_%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8&amp;diff=1020</id>
		<title>Составные Домены и Списки</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%94%D0%BE%D0%BC%D0%B5%D0%BD%D1%8B_%D0%B8_%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8&amp;diff=1020"/>
		<updated>2007-11-06T07:16:15Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В этом руководстве мы представим составные домены (иногда называемые алгебраическими структурами данных). Составные домены используются для обработки наборов данных как единого целого. Списки являются примером составных доменов. Они используются настолько часто, что получили даже специальную синтаксическую окраску.&lt;br /&gt;
&lt;br /&gt;
Составные домены и списки создаются с использованием встроенных (built-in) и других составных или списковых доменов. Справочная система Visual Prolog (Help) объясняет встроенные домены:&lt;br /&gt;
*integer - целые &lt;br /&gt;
*real - вещественные &lt;br /&gt;
*string - строковые&lt;br /&gt;
*symbol -символьные &lt;br /&gt;
*char - знаковые&lt;br /&gt;
*sring8 - строковые 8-разрядные &lt;br /&gt;
*pointer - указатели &lt;br /&gt;
*binary - двоичные (бинарные) &lt;br /&gt;
*boolean - булевские &lt;br /&gt;
*object - объекты&lt;br /&gt;
&lt;br /&gt;
==Составные домены и Функторы==&lt;br /&gt;
Составные домены позволяют рассматривать наборы данных как единое целое и при этом мы можете рассматривать их и раздельно. Рассмотрим, например, дату &amp;quot;October 15, 2003&amp;quot;. Она состоит из трех единиц информации – месяца, дня и года – но было бы полезно рассматривать дату как единое целое в виде древовидной структуры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;        DATE&lt;br /&gt;
         /|\&lt;br /&gt;
        / | \&lt;br /&gt;
       /  |  \&lt;br /&gt;
      /   |   \&lt;br /&gt;
  October 15  2003&amp;lt;/pre&amp;gt;&lt;br /&gt;
Это можно сделать путем объявления домена &amp;#039;&amp;#039;&amp;#039;date_cmp&amp;#039;&amp;#039;&amp;#039;, содержащего  данные &amp;#039;&amp;#039;&amp;#039;date&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    date_cmp = date(string Month, unsigned Day, unsigned Year).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и затем писать просто, то есть&lt;br /&gt;
&amp;lt;vip&amp;gt;D = date(&amp;quot;October&amp;quot;, 15, 2003),&amp;lt;/vip&amp;gt;&lt;br /&gt;
Это выглядит как факт Пролога, но это не так – это всего лишь значение, которое можно обрабатывать почти также, как строки или числа. Такая структура начинается с имени, обычно называемым функтор (functor) (в данном случае - date), за которым следуют три аргумента.&lt;br /&gt;
&lt;br /&gt;
Обращаем Ваше внимание на то, что функтор в Visual Prolog не имеет ничего общего с функциями в языках программирования. &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Функтор не вызывает никаких вычислений&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;, это всего лишь имя, которое идентифицирует составную величину и объединяет свои аргументы.&lt;br /&gt;
&lt;br /&gt;
Аргументы составной величины могут быть, в свою очередь, составными. К примеру, Вы можете думать о чьем-либо дне рождения как о структуре данных, так, как показано:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;            birthday&lt;br /&gt;
             /    \&lt;br /&gt;
            /      \&lt;br /&gt;
           /        \&lt;br /&gt;
          /          \&lt;br /&gt;
         /            \&lt;br /&gt;
   person             date&lt;br /&gt;
    /  \              / | \&lt;br /&gt;
   /    \            /  |  \&lt;br /&gt;
&amp;quot;Per&amp;quot; &amp;quot;Schultze&amp;quot;  &amp;quot;Apr&amp;quot; 14 1960&amp;lt;/pre&amp;gt;&lt;br /&gt;
На Прологе это может быть записано как:&lt;br /&gt;
&amp;lt;vip&amp;gt;birthday(person(&amp;quot;Per&amp;quot;, &amp;quot;Schultze&amp;quot;), date(&amp;quot;Apr&amp;quot;,14,1960)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
В этом примере видны две подчасти составного значения birthday: аргумент person(&amp;quot;Per&amp;quot;, &amp;quot;Schultze&amp;quot;) и аргумент date(&amp;quot;Apr&amp;quot;, 14, 1960). Функторами этих величин являются person и date.&lt;br /&gt;
&lt;br /&gt;
==Унификация Составных Доменов==&lt;br /&gt;
Значения составных доменов могут унифицироваться либо с одиночными переменными, либо с составными значениями, которые сопоставимы с ними (здесь возможны переменные, как части внутренней структуры). Это значин, что Вы можете использовать составные величины для передачи в виде целого набора данных, а затем разбирать их по частям путем операции унификации. К примеру,&lt;br /&gt;
&amp;lt;vip&amp;gt;date(&amp;quot;April&amp;quot;, 14, 1960)&amp;lt;/vip&amp;gt;&lt;br /&gt;
сопоставляется с переменной X и связывает переменную X с date(&amp;quot;April&amp;quot;,14,1960). Кроме того,&lt;br /&gt;
&amp;lt;vip&amp;gt;date(&amp;quot;April&amp;quot;, 14, 1960)&amp;lt;/vip&amp;gt;&lt;br /&gt;
сопоставляется с date(Mo,Da,Yr) и связывает Mo с &amp;quot;April&amp;quot;, Da с 14 и Yr с 1960.&lt;br /&gt;
===Использование Знака Равенства для Унификации Составных Доменов===&lt;br /&gt;
Visual Prolog выполняет унификацию в двух случаях. Первый - это когда вызов сопоставляется с головой клаузы. Второй - это при использовании знака &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;равно&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; (=), который в действительности является инфиксной формой предиката (предикат, который находится &amp;#039;&amp;#039;&amp;#039;между&amp;#039;&amp;#039;&amp;#039; своими аргументами, вместо того, чтобы быть &amp;#039;&amp;#039;&amp;#039;перед&amp;#039;&amp;#039;&amp;#039; ними).&lt;br /&gt;
&lt;br /&gt;
Visual Prolog осуществляет необходимые связывания для унификации значений по обе стороны от знака &amp;#039;&amp;#039;&amp;#039;равно&amp;#039;&amp;#039;&amp;#039;. Это полезно для выделения значений аргументов в составных величинах. Например, следующий код проверяет имеют ли два человека одинаковы фамилии, после чего второй человек получает тот же адрес, что и первый.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
    domains&lt;br /&gt;
        person = person(name, address).&lt;br /&gt;
        name = name(string First, string Last).&lt;br /&gt;
        address = addr(street, string City, string State).&lt;br /&gt;
        street = street(integer Number, string Street_name).&lt;br /&gt;
&lt;br /&gt;
    predicates&lt;br /&gt;
        run :().&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
    clauses&lt;br /&gt;
        run():-&lt;br /&gt;
            console::init(),&lt;br /&gt;
            P1 =  person(name(&amp;quot;Jim&amp;quot;, &amp;quot;Smith&amp;quot;),addr(street(5, &amp;quot;1st st&amp;quot;), &amp;quot;igo&amp;quot;, &amp;quot;CA&amp;quot;)),&lt;br /&gt;
            P1 = person(name(_, &amp;quot;Smith&amp;quot;), Address),&lt;br /&gt;
            P2 = person(name(&amp;quot;Jane&amp;quot;, &amp;quot;Smith&amp;quot;), Address),&lt;br /&gt;
            !,&lt;br /&gt;
            stdio::write(&amp;quot;P1 = &amp;quot;, P1, &amp;quot;\n&amp;quot;),&lt;br /&gt;
            stdio::write(&amp;quot;P2 = &amp;quot;, P2, &amp;quot;\n&amp;quot;)&lt;br /&gt;
            ;&lt;br /&gt;
            stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
    my::run.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Структура Данных Как Единое Целое==&lt;br /&gt;
Составные данные могут рассматриваться как одиночные значения в клаузах Пролога, что значительно упрощает программирование. Рассмотрим следующий факт&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
в котором утверждается, что John владеет книгой &amp;quot;From Here to Eternity&amp;quot;, написанной автором &amp;quot;James Jones&amp;quot;. В то же время можно написать&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
что может интерпретироваться как&lt;br /&gt;
John владеет лошадью с именем Blacky.&lt;br /&gt;
&lt;br /&gt;
Составными значениями в этих двух примерах являются&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;horse(&amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Если бы вместо этого было бы записаны два факта:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, &amp;quot;From Here to Eternity&amp;quot;).&lt;br /&gt;
owns(&amp;quot;John&amp;quot;, &amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
то нельзя было бы решить является ли Blacky названием книги или имнем лошади. С другой стороны, можно использовать первый компонент структуры – функтор, для обозначения различия между различными типами данных. В этом примере использовались, соответственно функторы book и horse для того, чтобы показать различие между данными.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Помните&amp;#039;&amp;#039;&amp;#039;: Составные данные состоят из функтора и аргументов, принадлежащих функтору, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;functor(argument1, argument2, ..., argumentN)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Пример Использования Составных Доменов===&lt;br /&gt;
Важным свойством составных доменов является возможность легкой передачи сгруппированных данных с помощью одного аргумента. Рассмотрим случай поддержки базы данных телефонных номеров. В эту базу данных мы хотим включать также дни рождения своих друзей и членов их семей. Ниже приведен фрагмент кода, который можно было бы написать для этого:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;predicates&lt;br /&gt;
    phone_list :(string First, string Last, string Phone, string Month, intege Day, integer Year) determ.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    phone_list(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;422-0208&amp;quot;, &amp;quot;aug&amp;quot;, 3, 1955).&lt;br /&gt;
    phone_list(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, &amp;quot;433-9906&amp;quot;, &amp;quot;may&amp;quot;, 12, 1962).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Анализируя данные, замечаем, что факт phone_list имеет шесть аргументов; пять из них могут разбиты на два составных домена:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;       person                birthday&lt;br /&gt;
        /   \                /  |  \&lt;br /&gt;
       /     \              /   |   \&lt;br /&gt;
First Name  Last Name    Month Day Year&amp;lt;/pre&amp;gt;&lt;br /&gt;
Было бы более полезно представить эти факты в виде составных доменов. Отступив на шаг, видим, что персона (person) является отношением, а имя (First Name) и фамилия (Last Name) являются его аргументами. Кроме того, день рождения является также отношением с тремя аргументами: месяц (month), день (day) и год (year). Представление этих отношений средствами Пролога аналогично уже упоминавшемуся&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, &amp;quot;From Here to Eternity&amp;quot;).&lt;br /&gt;
owns(&amp;quot;John&amp;quot;, &amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Теперь можно переписать нашу небольшую базу данных с использованием этих составных доменов в базе данных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    name = person(string First, string Last).&lt;br /&gt;
    birthday = b_date(string Month, integer Day, integer Year).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    phone_list :(name, string Ph_num, birthday) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    phone_list(person(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;), &amp;quot;422-0208&amp;quot;,b_date(&amp;quot;aug&amp;quot;, 3, 1955)).&lt;br /&gt;
    phone_list(person(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;), &amp;quot;433-9906&amp;quot;,b_date(&amp;quot;may&amp;quot;, 12, 1962)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этой программе появились объявления двух составных &amp;#039;&amp;#039;&amp;#039;доменов (domains)&amp;#039;&amp;#039;&amp;#039;. Мы детальнее рассмотрим эти составные структыры данных позднее в этой главе. Пока же мы обратим внимание на преимущества использования таких составных доменов.&lt;br /&gt;
&lt;br /&gt;
Предикат phone_list теперь содержит три аргумента вместо прежних шести. Иногда разбиение данных на составные структуры упрощает программу и облегчает обработку данных.&lt;br /&gt;
&lt;br /&gt;
Теперь добавим некоторые правила в программу. Предположим, нам надо получить список людей, чьи дни рождения выпадают на текущий месяц. Ниже приведена программа, решающая эту задачу; эта программа использует стандартный предикат date для получения текущей даты из внутренних часов компьютера. Предикат date возвращает текущий год (year), месяц (month) и день (day) часов компьютера.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  name = person(string First, string Last).&lt;br /&gt;
  birthday = b_date(string Month, integer Day, integer Year).&lt;br /&gt;
predicates&lt;br /&gt;
  phone_list :(name, string Ph_num, birthday) multi(o,o,o).&lt;br /&gt;
  get_months_birthdays :().&lt;br /&gt;
  convert_month :(string Name, integer Num) determ(i,o).&lt;br /&gt;
  check_birthday_month :(integer Month_num, birthday) determ.&lt;br /&gt;
  write_person :(name).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  get_months_birthdays() :-&lt;br /&gt;
    stdio::write(&amp;quot;****** This Month&amp;#039;s Birthday List *****\n&amp;quot;),&lt;br /&gt;
    stdio::write(&amp;quot; First Name\t\t Last Name\n&amp;quot;),&lt;br /&gt;
    stdio::write(&amp;quot;***********************************\n&amp;quot;),&lt;br /&gt;
    CurTime = time::new(),&lt;br /&gt;
    CurTime:getDate(_, ThisMonth, _),&lt;br /&gt;
    phone_list(Person, _, Date),&lt;br /&gt;
      check_birthday_month(ThisMonth, Date),&lt;br /&gt;
      write_person(Person),&lt;br /&gt;
    fail.&lt;br /&gt;
  get_months_birthdays() :-&lt;br /&gt;
    stdio::write(&amp;quot;\n Press any key to continue:\n&amp;quot;),&lt;br /&gt;
    _ = console::readChar().&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  write_person(person(FirstName, LastName)) :-&lt;br /&gt;
    stdio::write(&amp;quot;  &amp;quot;, FirstName, &amp;quot;\t\t &amp;quot;,  LastName, &amp;quot;\n&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  check_birthday_month(Mon, b_date(Month, _, _)) :-&lt;br /&gt;
    convert_month(Month, Month1),&lt;br /&gt;
    Mon = Month1.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  phone_list(person(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;), &amp;quot;11-1111&amp;quot;, b_date(&amp;quot;Jan&amp;quot;, 3, 1955)).&lt;br /&gt;
  phone_list(person(&amp;quot;Benjamin&amp;quot;, &amp;quot;Thomas&amp;quot;), &amp;quot;222-2222&amp;quot;, b_date(&amp;quot;Feb&amp;quot;, 5, 1965)).&lt;br /&gt;
  phone_list(person(&amp;quot;Ray&amp;quot;, &amp;quot;William&amp;quot;), &amp;quot;333-3333&amp;quot;, b_date(&amp;quot;Mar&amp;quot;, 3, 1955)).&lt;br /&gt;
  phone_list(person(&amp;quot;Tomas&amp;quot;, &amp;quot;Alfred&amp;quot;), &amp;quot;444-4444&amp;quot;, b_date(&amp;quot;Apr&amp;quot;, 29, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Chris&amp;quot;, &amp;quot;Gralm&amp;quot;), &amp;quot;555-5555&amp;quot;, b_date(&amp;quot;May&amp;quot;, 12, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Dastin&amp;quot;, &amp;quot;Robert&amp;quot;), &amp;quot;666-6666&amp;quot;, b_date(&amp;quot;Jun&amp;quot;, 17, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Anna&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;777-7777&amp;quot;, b_date(&amp;quot;Jul&amp;quot;, 2, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Naomi&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;888-8888&amp;quot;, b_date(&amp;quot;Aug&amp;quot;, 10, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Christina&amp;quot;, &amp;quot;Lynn&amp;quot;), &amp;quot;999-9999&amp;quot;, b_date(&amp;quot;Sep&amp;quot;, 25, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Kathy&amp;quot;, &amp;quot;Ann&amp;quot;), &amp;quot;110-1010&amp;quot;, b_date(&amp;quot;Oct&amp;quot;, 20, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Elizabeth&amp;quot;, &amp;quot;Ann&amp;quot;), &amp;quot;110-1111&amp;quot;, b_date(&amp;quot;Nov&amp;quot;, 9, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Aaron&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;110-1212&amp;quot;, b_date(&amp;quot;Dec&amp;quot;, 31, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Jenifer&amp;quot;, &amp;quot;Faitlin&amp;quot;), &amp;quot;888-8888&amp;quot;, b_date(&amp;quot;Aug&amp;quot;, 14, 1975)).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  convert_month(&amp;quot;Jan&amp;quot;, 1).&lt;br /&gt;
  convert_month(&amp;quot;Feb&amp;quot;, 2).&lt;br /&gt;
  convert_month(&amp;quot;Mar&amp;quot;, 3).&lt;br /&gt;
  convert_month(&amp;quot;Apr&amp;quot;, 4).&lt;br /&gt;
  convert_month(&amp;quot;May&amp;quot;, 5).&lt;br /&gt;
  convert_month(&amp;quot;Jun&amp;quot;, 6).&lt;br /&gt;
  convert_month(&amp;quot;Jul&amp;quot;, 7).&lt;br /&gt;
  convert_month(&amp;quot;Aug&amp;quot;, 8).&lt;br /&gt;
  convert_month(&amp;quot;Sep&amp;quot;, 9).&lt;br /&gt;
  convert_month(&amp;quot;Oct&amp;quot;, 10).&lt;br /&gt;
  convert_month(&amp;quot;Nov&amp;quot;, 11).&lt;br /&gt;
  convert_month(&amp;quot;Dec&amp;quot;, 12).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::get_months_birthdays().&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каким образом составные домены помогают в этой программе? Это несложно увидеть, просмотрев код. Основная часть обработки производится в предикате get_months_birthdays.&lt;br /&gt;
&lt;br /&gt;
*Сначала создается окно предикатом console::init()&lt;br /&gt;
*После этого пишется заголовок в окне, помогающий интерпретировать результаты.&lt;br /&gt;
*В предикате get_months_birthdays программа использует встроенный (built-in) предикат date, чтобы получить значение текущего месяца.&lt;br /&gt;
*Далее все в программе направлено на поиск в базе данных и получения списка людей, родившихся в текущем месяце. Прежде всего надо найти первого человека в базе данных. Вызов phone_list(Person, _, Date) связывает имя и фамилию человка с переменной Person путем связывания целого функтора person с этой переменной Person. Одновремиенно связывается день рождения человека с переменной Date.&amp;lt;br/&amp;gt; Обратите внимание, что для этого требуется всего одна переменная для запоминания полного имени человека и всего одна переменная Date для запоминания дня рождения. В этом и заключается мощность составных доменов.&lt;br /&gt;
*Теперь программа передает день рождения человека в виде одной переменной Date. Это происходит в следующей подцели, где программа передает значение текущего месяца (month), представленного целым числом, и день рождения (обрабатываемого человека) в предикат check_birthday_month.&lt;br /&gt;
*Посмотрите внимательно, что происходит. Visual Prolog вызывает предикат check_birthday_month с двуми параметрами: первый параметр связан с целым числом, а второй -  с термом birthday. В голове правила, определяющего предикат check_birthday_month, первый аргумент сопоставляется с переменной Mon. Второй аргумент, Date, сопоставляется с b_date(Month, _,_).&amp;lt;br/&amp;gt; Поскольку нас интересует только месяц рождения человека, то на месте как дня, так и года рождения мы используем анонимную переменную.&lt;br /&gt;
*Предикат check_birthday_month сначала преобразует строку с названием месяца в целочисленное значение. Как только это сделано, Visual Prolog может сравнивать значение текущего месяца со значением месяца рождения человека. Если такое сравнение положительно (успешно), то цель check_birthday_month считается успешной, и обработка продолжается. Если же сравнение неуспешно (fails) - обрабатываемый человек не родился в текущем месяце, то Visual Prolog начинает операцию отката для поиска другого возможного решения задачи.&lt;br /&gt;
*Следующая обрабатываемая подцель - write_person. У обрабатываемого человека день рождения выпадает на текущий месяц, следовательно можно печатать его имя в отчете. После печати информации, клауза завершается предикатом неуспешности (fail), что вызывает откат.&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Откат всегда распространяется вверх до ближайшего недетерминированного вызова и пытается передоказать вызов.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; В нашей программе последним недетерминированным вызовом является вызов phone_list. Именно здесь программа выбирает следующего человека для обработки. Если людей в списке (в базе данных) больше нет, то текущая клауза завершается неуспешно (fails); Visual Prolog теперь пытается доказать эту цель путем просмотра быза правил ниже. Поскольку есть еще одна клауза, определяющая get_months_birthdays, Visual Prolog пытается доказать обращение к get_months_birthdays путем доказательства подцелей этой другой части клаузы.&lt;br /&gt;
&lt;br /&gt;
=Декларирование Составных Доменов=&lt;br /&gt;
&lt;br /&gt;
В этом разделе мы покажем как определяются составные домены. После компиляции программы, содержащей следующие отношения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
можно запрашивать систему с помощью следующей цели:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
Переменная X может быть связана с различными типами данных - книгой, лошадью или возможно другим типом, который будет определен. Поскольку предикат owns теперь определён, больше его нельзя использовать в смысле старого определения:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns :(string, string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Второй аргумент больше не относится к домену string. Вместо этого необходимо сформулировать новое определение предиката, такое как, например&lt;br /&gt;
&amp;lt;vip&amp;gt;owns :(name, articles).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Вы можете теперь описать домен articles в секции domains как показано здесь:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  articles = &lt;br /&gt;
    book(string Title, string Author);&lt;br /&gt;
    horse(string Name).&lt;br /&gt;
/* Articles - это книги или лошади */&amp;lt;/vip&amp;gt;&lt;br /&gt;
Точка с запятой здесь читаются как &amp;#039;&amp;#039;or (или)&amp;#039;&amp;#039;. В нашем случае возможны две альтернативы: либо книга может быть определена своим названием и автором, либо лошадь определяется своим именем. Домены Title, Author и Name - все являются стандартными доменами string.&lt;br /&gt;
&lt;br /&gt;
Дополнительные альтернативы могут быть легко добавлены в декларацию домена. К примеру, articles мог бы включать лодку, дом, или банковский счёт. Для лодки, можно было бы использовать функтор без аргументов. С другой стороны, может возникнуть желание представлять баланс счета в качестве банковской записи. Тогда объявления домена articles расширяются до:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  articles = &lt;br /&gt;
    book(string Title, string Author);&lt;br /&gt;
    horse(string Name); &lt;br /&gt;
    boat; &lt;br /&gt;
    bankbook(real Balance).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Далее приведена полная программа, которая показывает как составной домен articles может быть использован в определениях предиката owns.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  articles =&lt;br /&gt;
    book(string Title, string Author) ;&lt;br /&gt;
    horse(string Name) ; &lt;br /&gt;
    boat ; &lt;br /&gt;
    bankbook(real Balance).&lt;br /&gt;
predicates&lt;br /&gt;
  owns :(string Name, articles) nondeterm(i,o) determ(i,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
  clauses&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, book(&amp;quot;A friend of the family&amp;quot;, &amp;quot;Irwin Shaw&amp;quot;)).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, boat).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, bankbook(1000)).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::owns(&amp;quot;John&amp;quot;, Thing),&lt;br /&gt;
    stdio::write(&amp;quot;Thing: &amp;quot;, Thing, &amp;quot;\n&amp;quot;),&lt;br /&gt;
  fail&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;The end.&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Загрузим теперь эту программу в IDE и запустим её. Visual Prolog теперь ответит:&lt;br /&gt;
&amp;lt;vip&amp;gt;Thing: book(&amp;quot;A friend of the family&amp;quot;,&amp;quot;Irwin Shaw&amp;quot;)&lt;br /&gt;
Thing: horse(&amp;quot;Blacky&amp;quot;)&lt;br /&gt;
Thing: boat()&lt;br /&gt;
Thing: bankbook(1000)&lt;br /&gt;
The end.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Итак: правила объявлений==&lt;br /&gt;
&lt;br /&gt;
Дадим теперь общее представление способа записи объявлений составных доменов:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
domain = &lt;br /&gt;
  alternative1(D, D, ...);&lt;br /&gt;
  alternative2(D, D, ...);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь alternative1 и alternative2 являются равновозможными (но различными) функторами. Обозначение (D, D, ...) представляет список имен, которые объявлены в любом месте либо относятся к стандартным типам доменов(таким как string, integer, real и т.д.).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Примечания:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*Алтернативы разделяются точкой с запятой.&lt;br /&gt;
*Каждая альтернатива состоит из фуктора и, возможно, списка доменов для соответствующих аргументов.&lt;br /&gt;
*Если функтор не имеет аргументов, его можно записывать как alternativeN или alternativeN( ).&lt;br /&gt;
&lt;br /&gt;
==Многоуровневые Составные Домены==&lt;br /&gt;
Visual Prolog позволяет организовывать многоуровневые составные домены. Например, в&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;The Ugly Duckling&amp;quot;, &amp;quot;Andersen&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
вместо использования фамилии автора, можно было бы использовать новую структуру, детализирующую автора глубже, включая имя и фамилию автора. Дав название новому составному домену author, теперь можно поменять описание книги на&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;The Ugly Duckling&amp;quot;, author(&amp;quot;Hans Christian&amp;quot;, &amp;quot;Andersen&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В старом объявлении домена&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(string Title, string Author).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
второй аргумент функтора book, представляющий автора может представлять только одиночное имя, но этого теперь недостаточно. Надо теперь объявить, что автор представляется составным доменом, образованным из имени и фамилии. Это делается декларацией домена:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;author = author(string First_name, string Last_name).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
что приводит к следующим объявлениям:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    articles = book(string Title, author Author); ...&lt;br /&gt;
        /* First level */&lt;br /&gt;
    author = author(string First_name, string Last_name).&lt;br /&gt;
        /* Second level */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Используя составные домены таким образом на разных уровнях, часто бывает полезно рисовать дерево:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;       book&lt;br /&gt;
       /  \&lt;br /&gt;
      /    \&lt;br /&gt;
  title   author&lt;br /&gt;
           /   \&lt;br /&gt;
          /     \&lt;br /&gt;
   First_name  Last_name&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Одна декларация домена всегда описывает только один уровень дерева, а не все дерево. В частности, домен book не может быть объявлен  такой декларацией:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Not allowed */&lt;br /&gt;
book = book(string Title, author(string First_name, string Last_name)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Объявления Составных Смешанных Доменов=&lt;br /&gt;
&lt;br /&gt;
Давайте обсудим три различных объявления доменов, которые можно включать в программы. Эти декларации позволяют использовать предикаты, которые&lt;br /&gt;
&lt;br /&gt;
*принимают аргументы более чем одного типа&lt;br /&gt;
*принимают переменное число аргументов одного типа&lt;br /&gt;
*принимают переменное число аргументов, каждый из которых может иметь более одного типа&lt;br /&gt;
&lt;br /&gt;
==Аргументы множественных типов==&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы предикат в Visual Prolog допускал использование аргументов различного типа, необходимо использовать функторные объявления. Следующий пример, клауза your_age clause будет воспринимать аргументы типа age, которые могут иметь тип string, real или integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    age = i(integer); r(real); s(string).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    your_age :(age).&lt;br /&gt;
clauses&lt;br /&gt;
    your_age(i(Age)) :- stdio::write(Age).&lt;br /&gt;
    your_age(r(Age)) :- stdio::write(Age).&lt;br /&gt;
    your_age(s(Age)) :- stdio::write(Age).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При этом Visual Prolog не допускает декларации вида:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Не допустимо. */&lt;br /&gt;
domains&lt;br /&gt;
    age = integer; real; string.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Списки==&lt;br /&gt;
&lt;br /&gt;
Предположим, мы обрабатываем данные о предметах, которые может преподавать учитель. Код может выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class predicates&lt;br /&gt;
    teacher :(string First_name, string Last_name, string Class) determ.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;english1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;math1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;history1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, &amp;quot;history2&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, &amp;quot;math2&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, &amp;quot;geometry&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь необходимо повторять имя преподавателя для каждого предмета, который он (она) может преподавать. Для каждого предмета необходимо добавлять такой факт в базу данных. Хотя в данной ситуации это не вызывает трудности, можно найти школу, в которой сотни предметов, и тогда поддержка такой структур данных становится утомительной. Тогда было бы удобно создать такой аргумент для предиката, который мог бы воспринимать один или несколько значений.&lt;br /&gt;
&lt;br /&gt;
В Прологе это делает &amp;#039;&amp;#039;&amp;#039;список&amp;#039;&amp;#039;&amp;#039;. В следующем коде аргумент class объявлен имеющим &amp;#039;&amp;#039;&amp;#039;тип список&amp;#039;&amp;#039;&amp;#039;. Покажем, как список представляется в Прологе.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    classes = string*.   /* объявляет списковый домен*/&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    teacher :(string First_name,  string Last_name,  classes Classes) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, [&amp;quot;english1&amp;quot;, &amp;quot;math1&amp;quot;, &amp;quot;history1&amp;quot;]).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, [&amp;quot;history2&amp;quot;, &amp;quot;math2&amp;quot;]).&lt;br /&gt;
    teacher(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, [&amp;quot;geometry&amp;quot;]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере код более лаконичен и проще читается, чем код предыдущего примера. Обратите внимание на объявление домена. Звездочка (*) означает, домен classes является списком строк. Так же легко можно объявить список целых:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Объявив домен, теперь просто его использовать, поместив его в качестве аргумента в декларации предиката в секции предикатов. Посмотрим пример использования списка целых:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    integer_list = integer*.&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    test_scores :&lt;br /&gt;
        (string First_name,&lt;br /&gt;
          string Last_name,&lt;br /&gt;
          integer_list Test_Scores)&lt;br /&gt;
          determ.&lt;br /&gt;
clauses&lt;br /&gt;
    test_scores(&amp;quot;Lisa&amp;quot;, &amp;quot;Lavender&amp;quot;, [86, 91, 75]).&lt;br /&gt;
    test_scores(&amp;quot;Libby&amp;quot;, &amp;quot;Dazzner&amp;quot;, [79, 75]).&lt;br /&gt;
    test_scores(&amp;quot;Jeff&amp;quot;, &amp;quot;Zheutlin&amp;quot;, []).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, в случае &amp;#039;&amp;#039;Jeff Zheutlin&amp;#039;&amp;#039; список не содержит никаких элементов.&lt;br /&gt;
&lt;br /&gt;
Еще один пример показывает как можно использовать списки для представления семейного дерева.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    tree_list = tree*.&lt;br /&gt;
    tree = tree(string Text, tree_list TreeList).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    family :(tree) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    family(tree(&amp;quot;Grandmother&amp;quot;,&lt;br /&gt;
            [tree(&amp;quot;John&amp;quot;,&lt;br /&gt;
                [tree(&amp;quot;Eric&amp;quot;, []),&lt;br /&gt;
                 tree(&amp;quot;Mark&amp;quot;, []),&lt;br /&gt;
                 tree(&amp;quot;Leonard&amp;quot;, []) ] ),&lt;br /&gt;
             tree(&amp;quot;Ellen&amp;quot;, [])&lt;br /&gt;
            ]&lt;br /&gt;
        )).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Заключение=&lt;br /&gt;
&lt;br /&gt;
В этом руководстве были рассмотрены важные понятия:&lt;br /&gt;
&lt;br /&gt;
*Программа на Visual Prolog может содержать множество типов значений: простых и составных, встроенных (стандартных) и определяемых в программе.&lt;br /&gt;
*Простые значения - это: числа, строки и т.д..&lt;br /&gt;
*&amp;#039;&amp;#039;Составные домены&amp;#039;&amp;#039; позволяют рассматривать структуру информации как единую сущность. Составной домен состоит из имени (известного как функтор) и, возможно, аргументов. Можно определить домен с несколькими альтернативными функторами.&lt;br /&gt;
*Функтор в Visual Prolog не есть то же, что функция в других языках программирования. &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Функтор не вызывает никаких вычислений.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; Это просто имя которое обозначает  составной домен и объединяет его аргументы.&lt;br /&gt;
*Составные значения могут обрабатываться и рассматриваться как одиночные переменные; функторы используются для создания различия между различными типами составных значений. Visual Prolog позволяет строить многоуровневые структурированные значения; Аргументами составных доменов могут быть опять-таки составные значения. Используя декларации составных смешанных доменов, можно использовать предикаты, которые:&lt;br /&gt;
**принимают аргументы нескольких различных типов (определяется объявлениями функторов).&lt;br /&gt;
**принимают переменное число аргументов, каждый определённого типа (объявления списков).&lt;br /&gt;
**принимают переменное число аргументов, часть из которых могут иметь более одного типа.&lt;br /&gt;
&lt;br /&gt;
=Ссылки=&lt;br /&gt;
[[en:Compound and List Domains]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%94%D0%BE%D0%BC%D0%B5%D0%BD%D1%8B_%D0%B8_%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8&amp;diff=1019</id>
		<title>Составные Домены и Списки</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%94%D0%BE%D0%BC%D0%B5%D0%BD%D1%8B_%D0%B8_%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8&amp;diff=1019"/>
		<updated>2007-11-06T07:14:16Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: /* Декларирование Составных Доменов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В этом руководстве мы представим составные домены (иногда называемые алгебраическими структурами данных). Составные домены используются для обработки наборов данных как единого целого. Списки являются примером составных доменов. Они используются настолько часто, что получили даже специальную синтаксическую окраску.&lt;br /&gt;
&lt;br /&gt;
Составные домены и списки создаются с использованием встроенных (built-in) и других составных или списковых доменов. Справочная система Visual Prolog (Help) объясняет встроенные домены:&lt;br /&gt;
*integer - целые &lt;br /&gt;
*real - вещественные &lt;br /&gt;
*string - строковые&lt;br /&gt;
*symbol -символьные &lt;br /&gt;
*char - знаковые&lt;br /&gt;
*sring8 - строковые 8-разрядные &lt;br /&gt;
*pointer - указатели &lt;br /&gt;
*binary - двоичные (бинарные) &lt;br /&gt;
*boolean - булевские &lt;br /&gt;
*object - объекты&lt;br /&gt;
&lt;br /&gt;
==Составные домены и Функторы==&lt;br /&gt;
Составные домены позволяют рассматривать наборы данных как единое целое и при этом мы можете рассматривать их и раздельно. Рассмотрим, например, дату &amp;quot;October 15, 2003&amp;quot;. Она состоит из трех единиц информации – месяца, дня и года – но было бы полезно рассматривать дату как единое целое в виде древовидной структуры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;        DATE&lt;br /&gt;
         /|\&lt;br /&gt;
        / | \&lt;br /&gt;
       /  |  \&lt;br /&gt;
      /   |   \&lt;br /&gt;
  October 15  2003&amp;lt;/pre&amp;gt;&lt;br /&gt;
Это можно сделать путем объявления домена &amp;#039;&amp;#039;&amp;#039;date_cmp&amp;#039;&amp;#039;&amp;#039;, содержащего  данные &amp;#039;&amp;#039;&amp;#039;date&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    date_cmp = date(string Month, unsigned Day, unsigned Year).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и затем писать просто, то есть&lt;br /&gt;
&amp;lt;vip&amp;gt;D = date(&amp;quot;October&amp;quot;, 15, 2003),&amp;lt;/vip&amp;gt;&lt;br /&gt;
Это выглядит как факт Пролога, но это не так – это всего лишь значение, которое можно обрабатывать почти также, как строки или числа. Такая структура начинается с имени, обычно называемым функтор (functor) (в данном случае - date), за которым следуют три аргумента.&lt;br /&gt;
&lt;br /&gt;
Обращаем Ваше внимание на то, что функтор в Visual Prolog не имеет ничего общего с функциями в языках программирования. &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Функтор не вызывает никаких вычислений&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;, это всего лишь имя, которое идентифицирует составную величину и объединяет свои аргументы.&lt;br /&gt;
&lt;br /&gt;
Аргументы составной величины могут быть, в свою очередь, составными. К примеру, Вы можете думать о чьем-либо дне рождения как о структуре данных, так, как показано:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;            birthday&lt;br /&gt;
             /    \&lt;br /&gt;
            /      \&lt;br /&gt;
           /        \&lt;br /&gt;
          /          \&lt;br /&gt;
         /            \&lt;br /&gt;
   person             date&lt;br /&gt;
    /  \              / | \&lt;br /&gt;
   /    \            /  |  \&lt;br /&gt;
&amp;quot;Per&amp;quot; &amp;quot;Schultze&amp;quot;  &amp;quot;Apr&amp;quot; 14 1960&amp;lt;/pre&amp;gt;&lt;br /&gt;
На Прологе это может быть записано как:&lt;br /&gt;
&amp;lt;vip&amp;gt;birthday(person(&amp;quot;Per&amp;quot;, &amp;quot;Schultze&amp;quot;), date(&amp;quot;Apr&amp;quot;,14,1960)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
В этом примере видны две подчасти составного значения birthday: аргумент person(&amp;quot;Per&amp;quot;, &amp;quot;Schultze&amp;quot;) и аргумент date(&amp;quot;Apr&amp;quot;, 14, 1960). Функторами этих величин являются person и date.&lt;br /&gt;
&lt;br /&gt;
==Унификация Составных Доменов==&lt;br /&gt;
Значения составных доменов могут унифицироваться либо с одиночными переменными, либо с составными значениями, которые сопоставимы с ними (здесь возможны переменные, как части внутренней структуры). Это значин, что Вы можете использовать составные величины для передачи в виде целого набора данных, а затем разбирать их по частям путем операции унификации. К примеру,&lt;br /&gt;
&amp;lt;vip&amp;gt;date(&amp;quot;April&amp;quot;, 14, 1960)&amp;lt;/vip&amp;gt;&lt;br /&gt;
сопоставляется с переменной X и связывает переменную X с date(&amp;quot;April&amp;quot;,14,1960). Кроме того,&lt;br /&gt;
&amp;lt;vip&amp;gt;date(&amp;quot;April&amp;quot;, 14, 1960)&amp;lt;/vip&amp;gt;&lt;br /&gt;
сопоставляется с date(Mo,Da,Yr) и связывает Mo с &amp;quot;April&amp;quot;, Da с 14 и Yr с 1960.&lt;br /&gt;
===Использование Знака Равенства для Унификации Составных Доменов===&lt;br /&gt;
Visual Prolog выполняет унификацию в двух случаях. Первый - это когда вызов сопоставляется с головой клаузы. Второй - это при использовании знака &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;равно&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; (=), который в действительности является инфиксной формой предиката (предикат, который находится &amp;#039;&amp;#039;&amp;#039;между&amp;#039;&amp;#039;&amp;#039; своими аргументами, вместо того, чтобы быть &amp;#039;&amp;#039;&amp;#039;перед&amp;#039;&amp;#039;&amp;#039; ними).&lt;br /&gt;
&lt;br /&gt;
Visual Prolog осуществляет необходимые связывания для унификации значений по обе стороны от знака &amp;#039;&amp;#039;&amp;#039;равно&amp;#039;&amp;#039;&amp;#039;. Это полезно для выделения значений аргументов в составных величинах. Например, следующий код проверяет имеют ли два человека одинаковы фамилии, после чего второй человек получает тот же адрес, что и первый.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
    domains&lt;br /&gt;
        person = person(name, address).&lt;br /&gt;
        name = name(string First, string Last).&lt;br /&gt;
        address = addr(street, string City, string State).&lt;br /&gt;
        street = street(integer Number, string Street_name).&lt;br /&gt;
&lt;br /&gt;
    predicates&lt;br /&gt;
        run :().&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
    clauses&lt;br /&gt;
        run():-&lt;br /&gt;
            console::init(),&lt;br /&gt;
            P1 =  person(name(&amp;quot;Jim&amp;quot;, &amp;quot;Smith&amp;quot;),addr(street(5, &amp;quot;1st st&amp;quot;), &amp;quot;igo&amp;quot;, &amp;quot;CA&amp;quot;)),&lt;br /&gt;
            P1 = person(name(_, &amp;quot;Smith&amp;quot;), Address),&lt;br /&gt;
            P2 = person(name(&amp;quot;Jane&amp;quot;, &amp;quot;Smith&amp;quot;), Address),&lt;br /&gt;
            !,&lt;br /&gt;
            stdio::write(&amp;quot;P1 = &amp;quot;, P1, &amp;quot;\n&amp;quot;),&lt;br /&gt;
            stdio::write(&amp;quot;P2 = &amp;quot;, P2, &amp;quot;\n&amp;quot;)&lt;br /&gt;
            ;&lt;br /&gt;
            stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
    my::run.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Структура Данных Как Единое Целое==&lt;br /&gt;
Составные данные могут рассматриваться как одиночные значения в клаузах Пролога, что значительно упрощает программирование. Рассмотрим следующий факт&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
в котором утверждается, что John владеет книгой &amp;quot;From Here to Eternity&amp;quot;, написанной автором &amp;quot;James Jones&amp;quot;. В то же время можно написать&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
что может интерпретироваться как&lt;br /&gt;
John владеет лошадью с именем Blacky.&lt;br /&gt;
&lt;br /&gt;
Составными значениями в этих двух примерах являются&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;horse(&amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Если бы вместо этого было бы записаны два факта:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, &amp;quot;From Here to Eternity&amp;quot;).&lt;br /&gt;
owns(&amp;quot;John&amp;quot;, &amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
то нельзя было бы решить является ли Blacky названием книги или имнем лошади. С другой стороны, можно использовать первый компонент структуры – функтор, для обозначения различия между различными типами данных. В этом примере использовались, соответственно функторы book и horse для того, чтобы показать различие между данными.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Помните&amp;#039;&amp;#039;&amp;#039;: Составные данные состоят из функтора и аргументов, принадлежащих функтору, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;functor(argument1, argument2, ..., argumentN)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Пример Использования Составных Доменов===&lt;br /&gt;
Важным свойством составных доменов является возможность легкой передачи сгруппированных данных с помощью одного аргумента. Рассмотрим случай поддержки базы данных телефонных номеров. В эту базу данных мы хотим включать также дни рождения своих друзей и членов их семей. Ниже приведен фрагмент кода, который можно было бы написать для этого:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;predicates&lt;br /&gt;
    phone_list :(string First, string Last, string Phone, string Month, intege Day, integer Year) determ.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    phone_list(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;422-0208&amp;quot;, &amp;quot;aug&amp;quot;, 3, 1955).&lt;br /&gt;
    phone_list(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, &amp;quot;433-9906&amp;quot;, &amp;quot;may&amp;quot;, 12, 1962).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Анализируя данные, замечаем, что факт phone_list имеет шесть аргументов; пять из них могут разбиты на два составных домена:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;       person                birthday&lt;br /&gt;
        /   \                /  |  \&lt;br /&gt;
       /     \              /   |   \&lt;br /&gt;
First Name  Last Name    Month Day Year&amp;lt;/pre&amp;gt;&lt;br /&gt;
Было бы более полезно представить эти факты в виде составных доменов. Отступив на шаг, видим, что персона (person) является отношением, а имя (First Name) и фамилия (Last Name) являются его аргументами. Кроме того, день рождения является также отношением с тремя аргументами: месяц (month), день (day) и год (year). Представление этих отношений средствами Пролога аналогично уже упоминавшемуся&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, &amp;quot;From Here to Eternity&amp;quot;).&lt;br /&gt;
owns(&amp;quot;John&amp;quot;, &amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Теперь можно переписать нашу небольшую базу данных с использованием этих составных доменов в базе данных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    name = person(string First, string Last).&lt;br /&gt;
    birthday = b_date(string Month, integer Day, integer Year).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    phone_list :(name, string Ph_num, birthday) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    phone_list(person(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;), &amp;quot;422-0208&amp;quot;,b_date(&amp;quot;aug&amp;quot;, 3, 1955)).&lt;br /&gt;
    phone_list(person(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;), &amp;quot;433-9906&amp;quot;,b_date(&amp;quot;may&amp;quot;, 12, 1962)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этой программе появились объявления двух составных &amp;#039;&amp;#039;&amp;#039;доменов (domains)&amp;#039;&amp;#039;&amp;#039;. Мы детальнее рассмотрим эти составные структыры данных позднее в этой главе. Пока же мы обратим внимание на преимущества использования таких составных доменов.&lt;br /&gt;
&lt;br /&gt;
Предикат phone_list теперь содержит три аргумента вместо прежних шести. Иногда разбиение данных на составные структуры упрощает программу и облегчает обработку данных.&lt;br /&gt;
&lt;br /&gt;
Теперь добавим некоторые правила в программу. Предположим, нам надо получить список людей, чьи дни рождения выпадают на текущий месяц. Ниже приведена программа, решающая эту задачу; эта программа использует стандартный предикат date для получения текущей даты из внутренних часов компьютера. Предикат date возвращает текущий год (year), месяц (month) и день (day) часов компьютера.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  name = person(string First, string Last).&lt;br /&gt;
  birthday = b_date(string Month, integer Day, integer Year).&lt;br /&gt;
predicates&lt;br /&gt;
  phone_list :(name, string Ph_num, birthday) multi(o,o,o).&lt;br /&gt;
  get_months_birthdays :().&lt;br /&gt;
  convert_month :(string Name, integer Num) determ(i,o).&lt;br /&gt;
  check_birthday_month :(integer Month_num, birthday) determ.&lt;br /&gt;
  write_person :(name).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  get_months_birthdays() :-&lt;br /&gt;
    stdio::write(&amp;quot;****** This Month&amp;#039;s Birthday List *****\n&amp;quot;),&lt;br /&gt;
    stdio::write(&amp;quot; First Name\t\t Last Name\n&amp;quot;),&lt;br /&gt;
    stdio::write(&amp;quot;***********************************\n&amp;quot;),&lt;br /&gt;
    CurTime = time::new(),&lt;br /&gt;
    CurTime:getDate(_, ThisMonth, _),&lt;br /&gt;
    phone_list(Person, _, Date),&lt;br /&gt;
      check_birthday_month(ThisMonth, Date),&lt;br /&gt;
      write_person(Person),&lt;br /&gt;
    fail.&lt;br /&gt;
  get_months_birthdays() :-&lt;br /&gt;
    stdio::write(&amp;quot;\n Press any key to continue:\n&amp;quot;),&lt;br /&gt;
    _ = console::readChar().&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  write_person(person(FirstName, LastName)) :-&lt;br /&gt;
    stdio::write(&amp;quot;  &amp;quot;, FirstName, &amp;quot;\t\t &amp;quot;,  LastName, &amp;quot;\n&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  check_birthday_month(Mon, b_date(Month, _, _)) :-&lt;br /&gt;
    convert_month(Month, Month1),&lt;br /&gt;
    Mon = Month1.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  phone_list(person(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;), &amp;quot;11-1111&amp;quot;, b_date(&amp;quot;Jan&amp;quot;, 3, 1955)).&lt;br /&gt;
  phone_list(person(&amp;quot;Benjamin&amp;quot;, &amp;quot;Thomas&amp;quot;), &amp;quot;222-2222&amp;quot;, b_date(&amp;quot;Feb&amp;quot;, 5, 1965)).&lt;br /&gt;
  phone_list(person(&amp;quot;Ray&amp;quot;, &amp;quot;William&amp;quot;), &amp;quot;333-3333&amp;quot;, b_date(&amp;quot;Mar&amp;quot;, 3, 1955)).&lt;br /&gt;
  phone_list(person(&amp;quot;Tomas&amp;quot;, &amp;quot;Alfred&amp;quot;), &amp;quot;444-4444&amp;quot;, b_date(&amp;quot;Apr&amp;quot;, 29, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Chris&amp;quot;, &amp;quot;Gralm&amp;quot;), &amp;quot;555-5555&amp;quot;, b_date(&amp;quot;May&amp;quot;, 12, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Dastin&amp;quot;, &amp;quot;Robert&amp;quot;), &amp;quot;666-6666&amp;quot;, b_date(&amp;quot;Jun&amp;quot;, 17, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Anna&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;777-7777&amp;quot;, b_date(&amp;quot;Jul&amp;quot;, 2, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Naomi&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;888-8888&amp;quot;, b_date(&amp;quot;Aug&amp;quot;, 10, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Christina&amp;quot;, &amp;quot;Lynn&amp;quot;), &amp;quot;999-9999&amp;quot;, b_date(&amp;quot;Sep&amp;quot;, 25, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Kathy&amp;quot;, &amp;quot;Ann&amp;quot;), &amp;quot;110-1010&amp;quot;, b_date(&amp;quot;Oct&amp;quot;, 20, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Elizabeth&amp;quot;, &amp;quot;Ann&amp;quot;), &amp;quot;110-1111&amp;quot;, b_date(&amp;quot;Nov&amp;quot;, 9, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Aaron&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;110-1212&amp;quot;, b_date(&amp;quot;Dec&amp;quot;, 31, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Jenifer&amp;quot;, &amp;quot;Faitlin&amp;quot;), &amp;quot;888-8888&amp;quot;, b_date(&amp;quot;Aug&amp;quot;, 14, 1975)).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  convert_month(&amp;quot;Jan&amp;quot;, 1).&lt;br /&gt;
  convert_month(&amp;quot;Feb&amp;quot;, 2).&lt;br /&gt;
  convert_month(&amp;quot;Mar&amp;quot;, 3).&lt;br /&gt;
  convert_month(&amp;quot;Apr&amp;quot;, 4).&lt;br /&gt;
  convert_month(&amp;quot;May&amp;quot;, 5).&lt;br /&gt;
  convert_month(&amp;quot;Jun&amp;quot;, 6).&lt;br /&gt;
  convert_month(&amp;quot;Jul&amp;quot;, 7).&lt;br /&gt;
  convert_month(&amp;quot;Aug&amp;quot;, 8).&lt;br /&gt;
  convert_month(&amp;quot;Sep&amp;quot;, 9).&lt;br /&gt;
  convert_month(&amp;quot;Oct&amp;quot;, 10).&lt;br /&gt;
  convert_month(&amp;quot;Nov&amp;quot;, 11).&lt;br /&gt;
  convert_month(&amp;quot;Dec&amp;quot;, 12).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::get_months_birthdays().&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каким образом составные домены помогают в этой программе? Это несложно увидеть, просмотрев код. Основная часть обработки производится в предикате get_months_birthdays.&lt;br /&gt;
&lt;br /&gt;
*Сначала создается окно предикатом console::init()&lt;br /&gt;
*После этого пишется заголовок в окне, помогающий интерпретировать результаты.&lt;br /&gt;
*В предикате get_months_birthdays программа использует встроенный (built-in) предикат date, чтобы получить значение текущего месяца.&lt;br /&gt;
*Далее все в программе направлено на поиск в базе данных и получения списка людей, родившихся в текущем месяце. Прежде всего надо найти первого человека в базе данных. Вызов phone_list(Person, _, Date) связывает имя и фамилию человка с переменной Person путем связывания целого функтора person с этой переменной Person. Одновремиенно связывается день рождения человека с переменной Date.&amp;lt;br/&amp;gt; Обратите внимание, что для этого требуется всего одна переменная для запоминания полного имени человека и всего одна переменная Date для запоминания дня рождения. В этом и заключается мощность составных доменов.&lt;br /&gt;
*Теперь программа передает день рождения человека в виде одной переменной Date. Это происходит в следующей подцели, где программа передает значение текущего месяца (month), представленного целым числом, и день рождения (обрабатываемого человека) в предикат check_birthday_month.&lt;br /&gt;
*Посмотрите внимательно, что происходит. Visual Prolog вызывает предикат check_birthday_month с двуми параметрами: первый параметр связан с целым числом, а второй -  с термом birthday. В голове правила, определяющего предикат check_birthday_month, первый аргумент сопоставляется с переменной Mon. Второй аргумент, Date, сопоставляется с b_date(Month, _,_).&amp;lt;br/&amp;gt; Поскольку нас интересует только месяц рождения человека, то на месте как дня, так и года рождения мы используем анонимную переменную.&lt;br /&gt;
*Предикат check_birthday_month сначала преобразует строку с названием месяца в целочисленное значение. Как только это сделано, Visual Prolog может сравнивать значение текущего месяца со значением месяца рождения человека. Если такое сравнение положительно (успешно), то цель check_birthday_month считается успешной, и обработка продолжается. Если же сравнение неуспешно (fails) - обрабатываемый человек не родился в текущем месяце, то Visual Prolog начинает операцию отката для поиска другого возможного решения задачи.&lt;br /&gt;
*Следующая обрабатываемая подцель - write_person. У обрабатываемого человека день рождения выпадает на текущий месяц, следовательно можно печатать его имя в отчете. После печати информации, клауза завершается предикатом неуспешности (fail), что вызывает откат.&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Откат всегда распространяется вверх до ближайшего недетерминированного вызова и пытается передоказать вызов.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; В нашей программе последним недетерминированным вызовом является вызов phone_list. Именно здесь программа выбирает следующего человека для обработки. Если людей в списке (в базе данных) больше нет, то текущая клауза завершается неуспешно (fails); Visual Prolog теперь пытается доказать эту цель путем просмотра быза правил ниже. Поскольку есть еще одна клауза, определяющая get_months_birthdays, Visual Prolog пытается доказать обращение к get_months_birthdays путем доказательства подцелей этой другой части клаузы.&lt;br /&gt;
&lt;br /&gt;
=Декларирование Составных Доменов=&lt;br /&gt;
&lt;br /&gt;
В этом разделе мы покажем как определяются составные домены. После компиляции программы, содержащей следующие отношения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
можно запрашивать систему с помощью следующей цели:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
Переменная X может быть связана с различными типами данных - книгой, лошадью или возможно другим типом, который будет определен. Поскольку предикат owns теперь определён, больше его нельзя использовать в смысле старого определения:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns :(string, string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Второй аргумент больше не относится к домену string. Вместо этого необходимо сформулировать новое определение предиката, такое как, например&lt;br /&gt;
&amp;lt;vip&amp;gt;owns :(name, articles).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Вы можете теперь описать домен articles в секции domains как показано здесь:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  articles = &lt;br /&gt;
    book(string Title, string Author);&lt;br /&gt;
    horse(string Name).&lt;br /&gt;
/* Articles - это книги или лошади */&amp;lt;/vip&amp;gt;&lt;br /&gt;
Точка с запятой здесь читаются как &amp;#039;&amp;#039;or (или)&amp;#039;&amp;#039;. В нашем случае возможны две альтернативы: либо книга может быть определена своим названием и автором, либо лошадь определяется своим именем. Домены Title, Author и Name - все являются стандартными доменами string.&lt;br /&gt;
&lt;br /&gt;
Дополнительные альтернативы могут быть легко добавлены в декларацию домена. К примеру, articles мог бы включать лодку, дом, или банковский счёт. Для лодки, можно было бы использовать функтор без аргументов. С другой стороны, может возникнуть желание представлять баланс счета в качестве банковской записи. Тогда объявления домена articles расширяются до:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  articles = &lt;br /&gt;
    book(string Title, string Author);&lt;br /&gt;
    horse(string Name); &lt;br /&gt;
    boat; &lt;br /&gt;
    bankbook(real Balance).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Далее приведена полная программа, которая показывает как составной домен articles может быть использован в определениях предиката owns.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  articles =&lt;br /&gt;
    book(string Title, string Author) ;&lt;br /&gt;
    horse(string Name) ; &lt;br /&gt;
    boat ; &lt;br /&gt;
    bankbook(real Balance).&lt;br /&gt;
predicates&lt;br /&gt;
  owns :(string Name, articles) nondeterm(i,o) determ(i,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
  clauses&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, book(&amp;quot;A friend of the family&amp;quot;, &amp;quot;Irwin Shaw&amp;quot;)).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, boat).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, bankbook(1000)).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::owns(&amp;quot;John&amp;quot;, Thing),&lt;br /&gt;
    stdio::write(&amp;quot;Thing: &amp;quot;, Thing, &amp;quot;\n&amp;quot;),&lt;br /&gt;
  fail&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;The end.&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Загрузим теперь эту программу в IDE и запустим её. Visual Prolog теперь ответит:&lt;br /&gt;
&amp;lt;vip&amp;gt;Thing: book(&amp;quot;A friend of the family&amp;quot;,&amp;quot;Irwin Shaw&amp;quot;)&lt;br /&gt;
Thing: horse(&amp;quot;Blacky&amp;quot;)&lt;br /&gt;
Thing: boat()&lt;br /&gt;
Thing: bankbook(1000)&lt;br /&gt;
The end.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Итак: правила объявлений==&lt;br /&gt;
&lt;br /&gt;
Дадим теперь общее представление способа записи объявлений составных доменов:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
domain = &lt;br /&gt;
  alternative1(D, D, ...);&lt;br /&gt;
  alternative2(D, D, ...);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь alternative1 и alternative2 являются равновозможными (но различными) функторами. Обозначение (D, D, ...) представляет список имен, которые объявлены в любом месте либо относятся к стандартным типам доменов(таким как string, integer, real и т.д.).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Примечания:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*Алтернативы разделяются точкой с запятой.&lt;br /&gt;
*Каждая альтернатива состоит из фуктора и, возможно, списка доменов для соответствующих аргументов.&lt;br /&gt;
*Если функтор не имеет аргументов, его можно записывать как alternativeN или alternativeN( ).&lt;br /&gt;
&lt;br /&gt;
==Многоуровневые Составные Домены==&lt;br /&gt;
Visual Prolog позволяет организовывать многоуровневые составные домены. Например, в&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;The Ugly Duckling&amp;quot;, &amp;quot;Andersen&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
вместо использования фамилии автора, можно было бы использовать новую структуру, детализирующую автора глубже, включая имя и фамилию автора. Дав название новому составному домену author, теперь можно поменять описание книги на&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;The Ugly Duckling&amp;quot;, author(&amp;quot;Hans Christian&amp;quot;, &amp;quot;Andersen&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В старом объявлении домена&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(string Title, string Author).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
второй аргумент функтора book, представляющий автора может представлять только одиночное имя, но этого теперь недостаточно. Надо теперь объявить, что автор представляется составным доменом, образованным из имени и фамилии. Это делается декларацией домена:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;author = author(string First_name, string Last_name).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
что приводит к следующим объявлениям:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    articles = book(string Title, author Author); ...&lt;br /&gt;
        /* First level */&lt;br /&gt;
    author = author(string First_name, string Last_name).&lt;br /&gt;
        /* Second level */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Используя составные домены таким образом на разных уровнях, часто бывает полезно рисовать дерево:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;       book&lt;br /&gt;
       /  \&lt;br /&gt;
      /    \&lt;br /&gt;
  title   author&lt;br /&gt;
           /   \&lt;br /&gt;
          /     \&lt;br /&gt;
   First_name  Last_name&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Одна декларация домена всегда описывает только один уровень дерева, а не все дерево. В частности, домен book не может быть объявлен  такой декларацией:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Not allowed */&lt;br /&gt;
book = book(string Title, author(string First_name, string Last_name)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Объявления Составных Смешанных Доменов=&lt;br /&gt;
&lt;br /&gt;
Давайте обсудим три различных объявления доменов, которые можно включать в программы. Эти декларации позволяют использовать предикаты, которые&lt;br /&gt;
&lt;br /&gt;
*принимают аргументы более чем одного типа&lt;br /&gt;
*принимают переменное число аргументов одного типа&lt;br /&gt;
*принимают переменное число аргументов, каждый из которых может иметь более одного типа&lt;br /&gt;
&lt;br /&gt;
==Аргументы множественных типов==&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы предикат в Visual Prolog допускал использование аргументов различного типа, необходимо использовать функторные объявления. Следующий пример, клауза your_age clause будет воспринимать аргументы типа age, которые могут иметь тип string, real или integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    age = i(integer); r(real); s(string).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    your_age :(age).&lt;br /&gt;
clauses&lt;br /&gt;
    your_age(i(Age)) :- stdio::write(Age).&lt;br /&gt;
    your_age(r(Age)) :- stdio::write(Age).&lt;br /&gt;
    your_age(s(Age)) :- stdio::write(Age).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При этом Visual Prolog не допускает декларации вида:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Не допустимо. */&lt;br /&gt;
domains&lt;br /&gt;
    age = integer; real; string.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Списки==&lt;br /&gt;
&lt;br /&gt;
Предположим, мы обрабатываем данные о предметах, которые может преподавать учитель. Код может выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class predicates&lt;br /&gt;
    teacher :(string First_name, string Last_name, string Class) determ.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;english1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;math1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;history1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, &amp;quot;history2&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, &amp;quot;math2&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, &amp;quot;geometry&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь необходимо повторять имя преподавателя для каждого предмета, который он (она) может преподавать. Для каждого предмета необходимо добавлять такой факт в базу данных. Хотя в данной ситуации это не вызывает трудности, можно найти школу, в которой сотни предметов, и тогда поддержка такой структур данных становится утомительной. Тогда было бы удобно создать такой аргумент для предиката, который мог бы воспринимать один или несколько значений.&lt;br /&gt;
&lt;br /&gt;
В Прологе это делает &amp;#039;&amp;#039;&amp;#039;список&amp;#039;&amp;#039;&amp;#039;. В следующем коде аргумент class объявлен имеющим &amp;#039;&amp;#039;&amp;#039;тип список&amp;#039;&amp;#039;&amp;#039;. Покажем, как список представляется в Прологе.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    classes = string*.   /* объявляет списковый домен*/&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    teacher :(string First_name,  string Last_name,  classes Classes) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, [&amp;quot;english1&amp;quot;, &amp;quot;math1&amp;quot;, &amp;quot;history1&amp;quot;]).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, [&amp;quot;history2&amp;quot;, &amp;quot;math2&amp;quot;]).&lt;br /&gt;
    teacher(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, [&amp;quot;geometry&amp;quot;]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере код более лаконичен и проще читается, чем код предыдущего примера. Обратите внимание на объявление домена. Звездочка (*) означает, домен classes является списком строк. Так же легко можно объявить список целых:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Объявив домен, теперь просто его использовать, поместив его в качестве аргумента в декларации предиката в секции предикатов. Посмотрим пример использования списка целых:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    integer_list = integer*.&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    test_scores :&lt;br /&gt;
        (string First_name,&lt;br /&gt;
          string Last_name,&lt;br /&gt;
          integer_list Test_Scores)&lt;br /&gt;
          determ.&lt;br /&gt;
clauses&lt;br /&gt;
    test_scores(&amp;quot;Lisa&amp;quot;, &amp;quot;Lavender&amp;quot;, [86, 91, 75]).&lt;br /&gt;
    test_scores(&amp;quot;Libby&amp;quot;, &amp;quot;Dazzner&amp;quot;, [79, 75]).&lt;br /&gt;
    test_scores(&amp;quot;Jeff&amp;quot;, &amp;quot;Zheutlin&amp;quot;, []).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, в случае &amp;#039;&amp;#039;Jeff Zheutlin&amp;#039;&amp;#039; список не содержит никаких элементов.&lt;br /&gt;
&lt;br /&gt;
Еще один пример показывает как можно использовать списки для представления семейного дерева.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    tree_list = tree*.&lt;br /&gt;
    tree = tree(string Text, tree_list TreeList).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    family :(tree) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    family(tree(&amp;quot;Grandmother&amp;quot;,&lt;br /&gt;
            [tree(&amp;quot;John&amp;quot;,&lt;br /&gt;
                [tree(&amp;quot;Eric&amp;quot;, []),&lt;br /&gt;
                 tree(&amp;quot;Mark&amp;quot;, []),&lt;br /&gt;
                 tree(&amp;quot;Leonard&amp;quot;, []) ] ),&lt;br /&gt;
             tree(&amp;quot;Ellen&amp;quot;, [])&lt;br /&gt;
            ]&lt;br /&gt;
        )).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Заключение=&lt;br /&gt;
&lt;br /&gt;
В этом руководстве были рассмотрены важные понятия:&lt;br /&gt;
&lt;br /&gt;
*Программа на Visual Prolog может содержать множество типов значений: простых и составных, встроенных (стандартных) и определяемых в программе.&lt;br /&gt;
*Простые значения - это: числа, строки и т.д..&lt;br /&gt;
*&amp;#039;&amp;#039;Составные домены&amp;#039;&amp;#039; позволяют рассматривать структуру информации как единую сущность. Составной домен состоит из имени (известного как функтор) и одного или более аргументов. Можно определить домен с несколькими альтернативными фукторами.&lt;br /&gt;
*Функтор в Visual Prolog не есть то же, что функция в других языках программирования. &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Функтор не вызывает никаких вычислений.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; Это просто имя которое обозначает  составной домен и объединяет его аргументы.&lt;br /&gt;
*Составные значения могут обрабатываться и рассматриваться как одиночные переменные; функторы используются для создания различия между различными типами составных значений. Visual Prolog позволяет строить многоуровневые структурированные значения; Аргументами составных доменов могут быть опять-таки составные значения. Используя декларации составных смешанных доменов, можно использовать предикаты, которые:&lt;br /&gt;
**принимают аргументы нескольких различных типов (определяется объявлениями функторов).&lt;br /&gt;
**принимают переменное число аргументов, каждый определенного типа (объявления списков).&lt;br /&gt;
**принимают переменное число аргументов, часть из которых могут иметь более одного типа.&lt;br /&gt;
&lt;br /&gt;
=Ссылки=&lt;br /&gt;
[[en:Compound and List Domains]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%94%D0%BE%D0%BC%D0%B5%D0%BD%D1%8B_%D0%B8_%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8&amp;diff=1018</id>
		<title>Составные Домены и Списки</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=%D0%A1%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%94%D0%BE%D0%BC%D0%B5%D0%BD%D1%8B_%D0%B8_%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%B8&amp;diff=1018"/>
		<updated>2007-11-06T07:13:04Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В этом руководстве мы представим составные домены (иногда называемые алгебраическими структурами данных). Составные домены используются для обработки наборов данных как единого целого. Списки являются примером составных доменов. Они используются настолько часто, что получили даже специальную синтаксическую окраску.&lt;br /&gt;
&lt;br /&gt;
Составные домены и списки создаются с использованием встроенных (built-in) и других составных или списковых доменов. Справочная система Visual Prolog (Help) объясняет встроенные домены:&lt;br /&gt;
*integer - целые &lt;br /&gt;
*real - вещественные &lt;br /&gt;
*string - строковые&lt;br /&gt;
*symbol -символьные &lt;br /&gt;
*char - знаковые&lt;br /&gt;
*sring8 - строковые 8-разрядные &lt;br /&gt;
*pointer - указатели &lt;br /&gt;
*binary - двоичные (бинарные) &lt;br /&gt;
*boolean - булевские &lt;br /&gt;
*object - объекты&lt;br /&gt;
&lt;br /&gt;
==Составные домены и Функторы==&lt;br /&gt;
Составные домены позволяют рассматривать наборы данных как единое целое и при этом мы можете рассматривать их и раздельно. Рассмотрим, например, дату &amp;quot;October 15, 2003&amp;quot;. Она состоит из трех единиц информации – месяца, дня и года – но было бы полезно рассматривать дату как единое целое в виде древовидной структуры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;        DATE&lt;br /&gt;
         /|\&lt;br /&gt;
        / | \&lt;br /&gt;
       /  |  \&lt;br /&gt;
      /   |   \&lt;br /&gt;
  October 15  2003&amp;lt;/pre&amp;gt;&lt;br /&gt;
Это можно сделать путем объявления домена &amp;#039;&amp;#039;&amp;#039;date_cmp&amp;#039;&amp;#039;&amp;#039;, содержащего  данные &amp;#039;&amp;#039;&amp;#039;date&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    date_cmp = date(string Month, unsigned Day, unsigned Year).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и затем писать просто, то есть&lt;br /&gt;
&amp;lt;vip&amp;gt;D = date(&amp;quot;October&amp;quot;, 15, 2003),&amp;lt;/vip&amp;gt;&lt;br /&gt;
Это выглядит как факт Пролога, но это не так – это всего лишь значение, которое можно обрабатывать почти также, как строки или числа. Такая структура начинается с имени, обычно называемым функтор (functor) (в данном случае - date), за которым следуют три аргумента.&lt;br /&gt;
&lt;br /&gt;
Обращаем Ваше внимание на то, что функтор в Visual Prolog не имеет ничего общего с функциями в языках программирования. &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Функтор не вызывает никаких вычислений&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;, это всего лишь имя, которое идентифицирует составную величину и объединяет свои аргументы.&lt;br /&gt;
&lt;br /&gt;
Аргументы составной величины могут быть, в свою очередь, составными. К примеру, Вы можете думать о чьем-либо дне рождения как о структуре данных, так, как показано:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;            birthday&lt;br /&gt;
             /    \&lt;br /&gt;
            /      \&lt;br /&gt;
           /        \&lt;br /&gt;
          /          \&lt;br /&gt;
         /            \&lt;br /&gt;
   person             date&lt;br /&gt;
    /  \              / | \&lt;br /&gt;
   /    \            /  |  \&lt;br /&gt;
&amp;quot;Per&amp;quot; &amp;quot;Schultze&amp;quot;  &amp;quot;Apr&amp;quot; 14 1960&amp;lt;/pre&amp;gt;&lt;br /&gt;
На Прологе это может быть записано как:&lt;br /&gt;
&amp;lt;vip&amp;gt;birthday(person(&amp;quot;Per&amp;quot;, &amp;quot;Schultze&amp;quot;), date(&amp;quot;Apr&amp;quot;,14,1960)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
В этом примере видны две подчасти составного значения birthday: аргумент person(&amp;quot;Per&amp;quot;, &amp;quot;Schultze&amp;quot;) и аргумент date(&amp;quot;Apr&amp;quot;, 14, 1960). Функторами этих величин являются person и date.&lt;br /&gt;
&lt;br /&gt;
==Унификация Составных Доменов==&lt;br /&gt;
Значения составных доменов могут унифицироваться либо с одиночными переменными, либо с составными значениями, которые сопоставимы с ними (здесь возможны переменные, как части внутренней структуры). Это значин, что Вы можете использовать составные величины для передачи в виде целого набора данных, а затем разбирать их по частям путем операции унификации. К примеру,&lt;br /&gt;
&amp;lt;vip&amp;gt;date(&amp;quot;April&amp;quot;, 14, 1960)&amp;lt;/vip&amp;gt;&lt;br /&gt;
сопоставляется с переменной X и связывает переменную X с date(&amp;quot;April&amp;quot;,14,1960). Кроме того,&lt;br /&gt;
&amp;lt;vip&amp;gt;date(&amp;quot;April&amp;quot;, 14, 1960)&amp;lt;/vip&amp;gt;&lt;br /&gt;
сопоставляется с date(Mo,Da,Yr) и связывает Mo с &amp;quot;April&amp;quot;, Da с 14 и Yr с 1960.&lt;br /&gt;
===Использование Знака Равенства для Унификации Составных Доменов===&lt;br /&gt;
Visual Prolog выполняет унификацию в двух случаях. Первый - это когда вызов сопоставляется с головой клаузы. Второй - это при использовании знака &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;равно&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; (=), который в действительности является инфиксной формой предиката (предикат, который находится &amp;#039;&amp;#039;&amp;#039;между&amp;#039;&amp;#039;&amp;#039; своими аргументами, вместо того, чтобы быть &amp;#039;&amp;#039;&amp;#039;перед&amp;#039;&amp;#039;&amp;#039; ними).&lt;br /&gt;
&lt;br /&gt;
Visual Prolog осуществляет необходимые связывания для унификации значений по обе стороны от знака &amp;#039;&amp;#039;&amp;#039;равно&amp;#039;&amp;#039;&amp;#039;. Это полезно для выделения значений аргументов в составных величинах. Например, следующий код проверяет имеют ли два человека одинаковы фамилии, после чего второй человек получает тот же адрес, что и первый.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
    domains&lt;br /&gt;
        person = person(name, address).&lt;br /&gt;
        name = name(string First, string Last).&lt;br /&gt;
        address = addr(street, string City, string State).&lt;br /&gt;
        street = street(integer Number, string Street_name).&lt;br /&gt;
&lt;br /&gt;
    predicates&lt;br /&gt;
        run :().&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
    clauses&lt;br /&gt;
        run():-&lt;br /&gt;
            console::init(),&lt;br /&gt;
            P1 =  person(name(&amp;quot;Jim&amp;quot;, &amp;quot;Smith&amp;quot;),addr(street(5, &amp;quot;1st st&amp;quot;), &amp;quot;igo&amp;quot;, &amp;quot;CA&amp;quot;)),&lt;br /&gt;
            P1 = person(name(_, &amp;quot;Smith&amp;quot;), Address),&lt;br /&gt;
            P2 = person(name(&amp;quot;Jane&amp;quot;, &amp;quot;Smith&amp;quot;), Address),&lt;br /&gt;
            !,&lt;br /&gt;
            stdio::write(&amp;quot;P1 = &amp;quot;, P1, &amp;quot;\n&amp;quot;),&lt;br /&gt;
            stdio::write(&amp;quot;P2 = &amp;quot;, P2, &amp;quot;\n&amp;quot;)&lt;br /&gt;
            ;&lt;br /&gt;
            stdio::write(&amp;quot;No solution&amp;quot;).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
    my::run.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Структура Данных Как Единое Целое==&lt;br /&gt;
Составные данные могут рассматриваться как одиночные значения в клаузах Пролога, что значительно упрощает программирование. Рассмотрим следующий факт&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
в котором утверждается, что John владеет книгой &amp;quot;From Here to Eternity&amp;quot;, написанной автором &amp;quot;James Jones&amp;quot;. В то же время можно написать&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
что может интерпретироваться как&lt;br /&gt;
John владеет лошадью с именем Blacky.&lt;br /&gt;
&lt;br /&gt;
Составными значениями в этих двух примерах являются&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;horse(&amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Если бы вместо этого было бы записаны два факта:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, &amp;quot;From Here to Eternity&amp;quot;).&lt;br /&gt;
owns(&amp;quot;John&amp;quot;, &amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
то нельзя было бы решить является ли Blacky названием книги или имнем лошади. С другой стороны, можно использовать первый компонент структуры – функтор, для обозначения различия между различными типами данных. В этом примере использовались, соответственно функторы book и horse для того, чтобы показать различие между данными.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Помните&amp;#039;&amp;#039;&amp;#039;: Составные данные состоят из функтора и аргументов, принадлежащих функтору, как показано ниже:&lt;br /&gt;
&amp;lt;vip&amp;gt;functor(argument1, argument2, ..., argumentN)&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Пример Использования Составных Доменов===&lt;br /&gt;
Важным свойством составных доменов является возможность легкой передачи сгруппированных данных с помощью одного аргумента. Рассмотрим случай поддержки базы данных телефонных номеров. В эту базу данных мы хотим включать также дни рождения своих друзей и членов их семей. Ниже приведен фрагмент кода, который можно было бы написать для этого:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;predicates&lt;br /&gt;
    phone_list :(string First, string Last, string Phone, string Month, intege Day, integer Year) determ.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    phone_list(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;422-0208&amp;quot;, &amp;quot;aug&amp;quot;, 3, 1955).&lt;br /&gt;
    phone_list(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, &amp;quot;433-9906&amp;quot;, &amp;quot;may&amp;quot;, 12, 1962).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Анализируя данные, замечаем, что факт phone_list имеет шесть аргументов; пять из них могут разбиты на два составных домена:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;       person                birthday&lt;br /&gt;
        /   \                /  |  \&lt;br /&gt;
       /     \              /   |   \&lt;br /&gt;
First Name  Last Name    Month Day Year&amp;lt;/pre&amp;gt;&lt;br /&gt;
Было бы более полезно представить эти факты в виде составных доменов. Отступив на шаг, видим, что персона (person) является отношением, а имя (First Name) и фамилия (Last Name) являются его аргументами. Кроме того, день рождения является также отношением с тремя аргументами: месяц (month), день (day) и год (year). Представление этих отношений средствами Пролога аналогично уже упоминавшемуся&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, &amp;quot;From Here to Eternity&amp;quot;).&lt;br /&gt;
owns(&amp;quot;John&amp;quot;, &amp;quot;Blacky&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Теперь можно переписать нашу небольшую базу данных с использованием этих составных доменов в базе данных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    name = person(string First, string Last).&lt;br /&gt;
    birthday = b_date(string Month, integer Day, integer Year).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    phone_list :(name, string Ph_num, birthday) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    phone_list(person(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;), &amp;quot;422-0208&amp;quot;,b_date(&amp;quot;aug&amp;quot;, 3, 1955)).&lt;br /&gt;
    phone_list(person(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;), &amp;quot;433-9906&amp;quot;,b_date(&amp;quot;may&amp;quot;, 12, 1962)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этой программе появились объявления двух составных &amp;#039;&amp;#039;&amp;#039;доменов (domains)&amp;#039;&amp;#039;&amp;#039;. Мы детальнее рассмотрим эти составные структыры данных позднее в этой главе. Пока же мы обратим внимание на преимущества использования таких составных доменов.&lt;br /&gt;
&lt;br /&gt;
Предикат phone_list теперь содержит три аргумента вместо прежних шести. Иногда разбиение данных на составные структуры упрощает программу и облегчает обработку данных.&lt;br /&gt;
&lt;br /&gt;
Теперь добавим некоторые правила в программу. Предположим, нам надо получить список людей, чьи дни рождения выпадают на текущий месяц. Ниже приведена программа, решающая эту задачу; эта программа использует стандартный предикат date для получения текущей даты из внутренних часов компьютера. Предикат date возвращает текущий год (year), месяц (month) и день (day) часов компьютера.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  name = person(string First, string Last).&lt;br /&gt;
  birthday = b_date(string Month, integer Day, integer Year).&lt;br /&gt;
predicates&lt;br /&gt;
  phone_list :(name, string Ph_num, birthday) multi(o,o,o).&lt;br /&gt;
  get_months_birthdays :().&lt;br /&gt;
  convert_month :(string Name, integer Num) determ(i,o).&lt;br /&gt;
  check_birthday_month :(integer Month_num, birthday) determ.&lt;br /&gt;
  write_person :(name).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  get_months_birthdays() :-&lt;br /&gt;
    stdio::write(&amp;quot;****** This Month&amp;#039;s Birthday List *****\n&amp;quot;),&lt;br /&gt;
    stdio::write(&amp;quot; First Name\t\t Last Name\n&amp;quot;),&lt;br /&gt;
    stdio::write(&amp;quot;***********************************\n&amp;quot;),&lt;br /&gt;
    CurTime = time::new(),&lt;br /&gt;
    CurTime:getDate(_, ThisMonth, _),&lt;br /&gt;
    phone_list(Person, _, Date),&lt;br /&gt;
      check_birthday_month(ThisMonth, Date),&lt;br /&gt;
      write_person(Person),&lt;br /&gt;
    fail.&lt;br /&gt;
  get_months_birthdays() :-&lt;br /&gt;
    stdio::write(&amp;quot;\n Press any key to continue:\n&amp;quot;),&lt;br /&gt;
    _ = console::readChar().&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  write_person(person(FirstName, LastName)) :-&lt;br /&gt;
    stdio::write(&amp;quot;  &amp;quot;, FirstName, &amp;quot;\t\t &amp;quot;,  LastName, &amp;quot;\n&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  check_birthday_month(Mon, b_date(Month, _, _)) :-&lt;br /&gt;
    convert_month(Month, Month1),&lt;br /&gt;
    Mon = Month1.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  phone_list(person(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;), &amp;quot;11-1111&amp;quot;, b_date(&amp;quot;Jan&amp;quot;, 3, 1955)).&lt;br /&gt;
  phone_list(person(&amp;quot;Benjamin&amp;quot;, &amp;quot;Thomas&amp;quot;), &amp;quot;222-2222&amp;quot;, b_date(&amp;quot;Feb&amp;quot;, 5, 1965)).&lt;br /&gt;
  phone_list(person(&amp;quot;Ray&amp;quot;, &amp;quot;William&amp;quot;), &amp;quot;333-3333&amp;quot;, b_date(&amp;quot;Mar&amp;quot;, 3, 1955)).&lt;br /&gt;
  phone_list(person(&amp;quot;Tomas&amp;quot;, &amp;quot;Alfred&amp;quot;), &amp;quot;444-4444&amp;quot;, b_date(&amp;quot;Apr&amp;quot;, 29, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Chris&amp;quot;, &amp;quot;Gralm&amp;quot;), &amp;quot;555-5555&amp;quot;, b_date(&amp;quot;May&amp;quot;, 12, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Dastin&amp;quot;, &amp;quot;Robert&amp;quot;), &amp;quot;666-6666&amp;quot;, b_date(&amp;quot;Jun&amp;quot;, 17, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Anna&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;777-7777&amp;quot;, b_date(&amp;quot;Jul&amp;quot;, 2, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Naomi&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;888-8888&amp;quot;, b_date(&amp;quot;Aug&amp;quot;, 10, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Christina&amp;quot;, &amp;quot;Lynn&amp;quot;), &amp;quot;999-9999&amp;quot;, b_date(&amp;quot;Sep&amp;quot;, 25, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Kathy&amp;quot;, &amp;quot;Ann&amp;quot;), &amp;quot;110-1010&amp;quot;, b_date(&amp;quot;Oct&amp;quot;, 20, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Elizabeth&amp;quot;, &amp;quot;Ann&amp;quot;), &amp;quot;110-1111&amp;quot;, b_date(&amp;quot;Nov&amp;quot;, 9, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Aaron&amp;quot;, &amp;quot;Friend&amp;quot;), &amp;quot;110-1212&amp;quot;, b_date(&amp;quot;Dec&amp;quot;, 31, 1975)).&lt;br /&gt;
  phone_list(person(&amp;quot;Jenifer&amp;quot;, &amp;quot;Faitlin&amp;quot;), &amp;quot;888-8888&amp;quot;, b_date(&amp;quot;Aug&amp;quot;, 14, 1975)).&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
  convert_month(&amp;quot;Jan&amp;quot;, 1).&lt;br /&gt;
  convert_month(&amp;quot;Feb&amp;quot;, 2).&lt;br /&gt;
  convert_month(&amp;quot;Mar&amp;quot;, 3).&lt;br /&gt;
  convert_month(&amp;quot;Apr&amp;quot;, 4).&lt;br /&gt;
  convert_month(&amp;quot;May&amp;quot;, 5).&lt;br /&gt;
  convert_month(&amp;quot;Jun&amp;quot;, 6).&lt;br /&gt;
  convert_month(&amp;quot;Jul&amp;quot;, 7).&lt;br /&gt;
  convert_month(&amp;quot;Aug&amp;quot;, 8).&lt;br /&gt;
  convert_month(&amp;quot;Sep&amp;quot;, 9).&lt;br /&gt;
  convert_month(&amp;quot;Oct&amp;quot;, 10).&lt;br /&gt;
  convert_month(&amp;quot;Nov&amp;quot;, 11).&lt;br /&gt;
  convert_month(&amp;quot;Dec&amp;quot;, 12).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::get_months_birthdays().&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каким образом составные домены помогают в этой программе? Это несложно увидеть, просмотрев код. Основная часть обработки производится в предикате get_months_birthdays.&lt;br /&gt;
&lt;br /&gt;
*Сначала создается окно предикатом console::init()&lt;br /&gt;
*После этого пишется заголовок в окне, помогающий интерпретировать результаты.&lt;br /&gt;
*В предикате get_months_birthdays программа использует встроенный (built-in) предикат date, чтобы получить значение текущего месяца.&lt;br /&gt;
*Далее все в программе направлено на поиск в базе данных и получения списка людей, родившихся в текущем месяце. Прежде всего надо найти первого человека в базе данных. Вызов phone_list(Person, _, Date) связывает имя и фамилию человка с переменной Person путем связывания целого функтора person с этой переменной Person. Одновремиенно связывается день рождения человека с переменной Date.&amp;lt;br/&amp;gt; Обратите внимание, что для этого требуется всего одна переменная для запоминания полного имени человека и всего одна переменная Date для запоминания дня рождения. В этом и заключается мощность составных доменов.&lt;br /&gt;
*Теперь программа передает день рождения человека в виде одной переменной Date. Это происходит в следующей подцели, где программа передает значение текущего месяца (month), представленного целым числом, и день рождения (обрабатываемого человека) в предикат check_birthday_month.&lt;br /&gt;
*Посмотрите внимательно, что происходит. Visual Prolog вызывает предикат check_birthday_month с двуми параметрами: первый параметр связан с целым числом, а второй -  с термом birthday. В голове правила, определяющего предикат check_birthday_month, первый аргумент сопоставляется с переменной Mon. Второй аргумент, Date, сопоставляется с b_date(Month, _,_).&amp;lt;br/&amp;gt; Поскольку нас интересует только месяц рождения человека, то на месте как дня, так и года рождения мы используем анонимную переменную.&lt;br /&gt;
*Предикат check_birthday_month сначала преобразует строку с названием месяца в целочисленное значение. Как только это сделано, Visual Prolog может сравнивать значение текущего месяца со значением месяца рождения человека. Если такое сравнение положительно (успешно), то цель check_birthday_month считается успешной, и обработка продолжается. Если же сравнение неуспешно (fails) - обрабатываемый человек не родился в текущем месяце, то Visual Prolog начинает операцию отката для поиска другого возможного решения задачи.&lt;br /&gt;
*Следующая обрабатываемая подцель - write_person. У обрабатываемого человека день рождения выпадает на текущий месяц, следовательно можно печатать его имя в отчете. После печати информации, клауза завершается предикатом неуспешности (fail), что вызывает откат.&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Откат всегда распространяется вверх до ближайшего недетерминированного вызова и пытается передоказать вызов.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; В нашей программе последним недетерминированным вызовом является вызов phone_list. Именно здесь программа выбирает следующего человека для обработки. Если людей в списке (в базе данных) больше нет, то текущая клауза завершается неуспешно (fails); Visual Prolog теперь пытается доказать эту цель путем просмотра быза правил ниже. Поскольку есть еще одна клауза, определяющая get_months_birthdays, Visual Prolog пытается доказать обращение к get_months_birthdays путем доказательства подцелей этой другой части клаузы.&lt;br /&gt;
&lt;br /&gt;
=Декларирование Составных Доменов=&lt;br /&gt;
&lt;br /&gt;
В этом разделе мы покажем как определяются составные домены. После компиляции программы, содержащей следующие отношения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, book(&amp;quot;From Here to Eternity&amp;quot;, &amp;quot;James Jones&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
и&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
можно запрашивать систему с помощью следующей цели:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns(&amp;quot;John&amp;quot;, X)&amp;lt;/vip&amp;gt;&lt;br /&gt;
Переменная X может быть связана с различными типами данных - книгой, лошадью или возможно другим типом, который будет определен. Поскольку предикат owns теперь определен, больше его нельзя использовать в смысле старого определения:&lt;br /&gt;
&amp;lt;vip&amp;gt;owns :(string, string).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Второй аргумент больше не относится к домену string. Вместо этого необходимо сформулировать новое определение предиката, такое как, например&lt;br /&gt;
&amp;lt;vip&amp;gt;owns :(name, articles).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Вы можете теперь описать домен articles в секции domains как показано здесь:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  articles = &lt;br /&gt;
    book(string Title, string Author);&lt;br /&gt;
    horse(string Name).&lt;br /&gt;
/* Articles - это книги или лошади */&amp;lt;/vip&amp;gt;&lt;br /&gt;
Точка с запятой здесь читаются как &amp;#039;&amp;#039;or (или)&amp;#039;&amp;#039;. В нашем случае возможны две альтернативы: либо книга может быть определена своим названием и автором, либо лошадь определяется своим именем. Домены Title, Author и Name - все являются стандартными доменами string.&lt;br /&gt;
&lt;br /&gt;
Дополнительные альтернативы могут быть легко добавлены в декларацию домена. К примеру, articles мог бы включать лодку, дом, или банковский счет. Для лодки, можно было бы использовать функтор без аргументов. С другой стороны, может возникнуть желание представлять баланс счета в качестве банковской записи. Тогда объявления домена articles расширяются до:&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
  articles = &lt;br /&gt;
    book(string Title, string Author);&lt;br /&gt;
    horse(string Name); &lt;br /&gt;
    boat; &lt;br /&gt;
    bankbook(real Balance).&amp;lt;/vip&amp;gt;&lt;br /&gt;
Далее приведена полная программа, которая показывает как составной домен articles может быть использован в определениях предиката owns.&lt;br /&gt;
&amp;lt;vip&amp;gt;class my&lt;br /&gt;
domains&lt;br /&gt;
  articles =&lt;br /&gt;
    book(string Title, string Author) ;&lt;br /&gt;
    horse(string Name) ; &lt;br /&gt;
    boat ; &lt;br /&gt;
    bankbook(real Balance).&lt;br /&gt;
predicates&lt;br /&gt;
  owns :(string Name, articles) nondeterm(i,o) determ(i,i).&lt;br /&gt;
end class&lt;br /&gt;
&lt;br /&gt;
implement my&lt;br /&gt;
  clauses&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, book(&amp;quot;A friend of the family&amp;quot;, &amp;quot;Irwin Shaw&amp;quot;)).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, horse(&amp;quot;Blacky&amp;quot;)).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, boat).&lt;br /&gt;
    owns(&amp;quot;John&amp;quot;, bankbook(1000)).&lt;br /&gt;
end implement&lt;br /&gt;
&lt;br /&gt;
goal&lt;br /&gt;
  console::init(),&lt;br /&gt;
  my::owns(&amp;quot;John&amp;quot;, Thing),&lt;br /&gt;
    stdio::write(&amp;quot;Thing: &amp;quot;, Thing, &amp;quot;\n&amp;quot;),&lt;br /&gt;
  fail&lt;br /&gt;
  ;&lt;br /&gt;
  stdio::write(&amp;quot;The end.&amp;quot;).&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Загрузим теперь эту программу в IDE и запустим ее. Visual Prolog теперь ответит:&lt;br /&gt;
&amp;lt;vip&amp;gt;Thing: book(&amp;quot;A friend of the family&amp;quot;,&amp;quot;Irwin Shaw&amp;quot;)&lt;br /&gt;
Thing: horse(&amp;quot;Blacky&amp;quot;)&lt;br /&gt;
Thing: boat()&lt;br /&gt;
Thing: bankbook(1000)&lt;br /&gt;
The end.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Итак: правила объявлений==&lt;br /&gt;
&lt;br /&gt;
Дадим теперь общее представление способа записи объявлений составных доменов:&lt;br /&gt;
&amp;lt;vip&amp;gt;&lt;br /&gt;
domain = &lt;br /&gt;
  alternative1(D, D, ...);&lt;br /&gt;
  alternative2(D, D, ...);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/vip&amp;gt;&lt;br /&gt;
Здесь alternative1 и alternative2 являются равновозможными (но различными) функторами. Обозначение (D, D, ...) представляет список имен, которые объявлены в любом месте либо относятся к стандартным типам доменов(таким как string, integer, real и т.д.).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Примечания:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*Алтернативы разделяются точкой с запятой.&lt;br /&gt;
*Каждая алттернатива состоит из фуктора и, возможно, списка доменов для соответствующих аргументов.&lt;br /&gt;
*Если функтор не имеет аргументов, его можно записывать как alternativeN или alternativeN( ).&lt;br /&gt;
&lt;br /&gt;
==Многоуровневые Составные Домены==&lt;br /&gt;
Visual Prolog позволяет организовывать многоуровневые составные домены. Например, в&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;The Ugly Duckling&amp;quot;, &amp;quot;Andersen&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
вместо использования фамилии автора, можно было бы использвать новую структуру, детализирующую автора глубже, включая имя и фамилию автора. Дав название новому составному домену author, теперь можно поменять описание книги на&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(&amp;quot;The Ugly Duckling&amp;quot;, author(&amp;quot;Hans Christian&amp;quot;, &amp;quot;Andersen&amp;quot;)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В старом объявлении домена&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;book(string Title, string Author).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
второй аргумент функтора book, представляющий автора может представлять только одиночное имя, но этого теперь недостаточно. Надо теперь объявить, что автор представляется составным доменом, образованным из имени и фамилии. Это делается декларацией домена:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;author = author(string First_name, string Last_name).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
что приводит к следующим объявлениям:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    articles = book(string Title, author Author); ...&lt;br /&gt;
        /* First level */&lt;br /&gt;
    author = author(string First_name, string Last_name).&lt;br /&gt;
        /* Second level */&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Используя составные домены таким образом на разных уровнях, часто бывает полезно рисовать дерево:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;       book&lt;br /&gt;
       /  \&lt;br /&gt;
      /    \&lt;br /&gt;
  title   author&lt;br /&gt;
           /   \&lt;br /&gt;
          /     \&lt;br /&gt;
   First_name  Last_name&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Одна декларация домена всегда описывает только один уровень дерева, а не все дерево. В частности, домен book не может быть объявлен  такой декларацией:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Not allowed */&lt;br /&gt;
book = book(string Title, author(string First_name, string Last_name)).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Объявления Составных Смешанных Доменов=&lt;br /&gt;
&lt;br /&gt;
Давайте обсудим три различных объявления доменов, которые можно включать в программы. Эти декларации позволяют использовать предикаты, которые&lt;br /&gt;
&lt;br /&gt;
*принимают аргументы более чем одного типа&lt;br /&gt;
*принимают переменное число аргументов одного типа&lt;br /&gt;
*принимают переменное число аргументов, каждый из которых может иметь более одного типа&lt;br /&gt;
&lt;br /&gt;
==Аргументы множественных типов==&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы предикат в Visual Prolog допускал использование аргументов различного типа, необходимо использовать функторные объявления. Следующий пример, клауза your_age clause будет воспринимать аргументы типа age, которые могут иметь тип string, real или integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    age = i(integer); r(real); s(string).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    your_age :(age).&lt;br /&gt;
clauses&lt;br /&gt;
    your_age(i(Age)) :- stdio::write(Age).&lt;br /&gt;
    your_age(r(Age)) :- stdio::write(Age).&lt;br /&gt;
    your_age(s(Age)) :- stdio::write(Age).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При этом Visual Prolog не допускает декларации вида:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;/* Не допустимо. */&lt;br /&gt;
domains&lt;br /&gt;
    age = integer; real; string.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Списки==&lt;br /&gt;
&lt;br /&gt;
Предположим, мы обрабатываем данные о предметах, которые может преподавать учитель. Код может выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;class predicates&lt;br /&gt;
    teacher :(string First_name, string Last_name, string Class) determ.&lt;br /&gt;
&lt;br /&gt;
clauses&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;english1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;math1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, &amp;quot;history1&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, &amp;quot;history2&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, &amp;quot;math2&amp;quot;).&lt;br /&gt;
    teacher(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, &amp;quot;geometry&amp;quot;).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь необходимо повторять имя преподавателя для каждого предмета, который он (она) может преподавать. Для каждого предмета необходимо добавлять такой факт в базу данных. Хотя в данной ситуации это не вызывает трудности, можно найти школу, в которой сотни предметов, и тогда поддержка такой структур данных становится утомительной. Тогда было бы удобно создать такой аргумент для предиката, который мог бы воспринимать один или несколько значений.&lt;br /&gt;
&lt;br /&gt;
В Прологе это делает &amp;#039;&amp;#039;&amp;#039;список&amp;#039;&amp;#039;&amp;#039;. В следующем коде аргумент class объявлен имеющим &amp;#039;&amp;#039;&amp;#039;тип список&amp;#039;&amp;#039;&amp;#039;. Покажем, как список представляется в Прологе.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    classes = string*.   /* объявляет списковый домен*/&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    teacher :(string First_name,  string Last_name,  classes Classes) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    teacher(&amp;quot;Ed&amp;quot;, &amp;quot;Willis&amp;quot;, [&amp;quot;english1&amp;quot;, &amp;quot;math1&amp;quot;, &amp;quot;history1&amp;quot;]).&lt;br /&gt;
    teacher(&amp;quot;Mary&amp;quot;, &amp;quot;Maker&amp;quot;, [&amp;quot;history2&amp;quot;, &amp;quot;math2&amp;quot;]).&lt;br /&gt;
    teacher(&amp;quot;Chris&amp;quot;, &amp;quot;Grahm&amp;quot;, [&amp;quot;geometry&amp;quot;]).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере код более лаконичен и проще читается, чем код предыдущего примера. Обратите внимание на объявление домена. Звездочка (*) означает, домен classes является списком строк. Так же легко можно объявить список целых:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    integer_list = integer*.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Объявив домен, теперь просто его использовать, поместив его в качестве аргумента в декларации предиката в секции предикатов. Посмотрим пример использования списка целых:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    integer_list = integer*.&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    test_scores :&lt;br /&gt;
        (string First_name,&lt;br /&gt;
          string Last_name,&lt;br /&gt;
          integer_list Test_Scores)&lt;br /&gt;
          determ.&lt;br /&gt;
clauses&lt;br /&gt;
    test_scores(&amp;quot;Lisa&amp;quot;, &amp;quot;Lavender&amp;quot;, [86, 91, 75]).&lt;br /&gt;
    test_scores(&amp;quot;Libby&amp;quot;, &amp;quot;Dazzner&amp;quot;, [79, 75]).&lt;br /&gt;
    test_scores(&amp;quot;Jeff&amp;quot;, &amp;quot;Zheutlin&amp;quot;, []).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, в случае &amp;#039;&amp;#039;Jeff Zheutlin&amp;#039;&amp;#039; список не содержит никаких элементов.&lt;br /&gt;
&lt;br /&gt;
Еще один пример показывает как можно использовать списки для представления семейного дерева.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    tree_list = tree*.&lt;br /&gt;
    tree = tree(string Text, tree_list TreeList).&lt;br /&gt;
&lt;br /&gt;
class predicates&lt;br /&gt;
    family :(tree) determ.&lt;br /&gt;
clauses&lt;br /&gt;
    family(tree(&amp;quot;Grandmother&amp;quot;,&lt;br /&gt;
            [tree(&amp;quot;John&amp;quot;,&lt;br /&gt;
                [tree(&amp;quot;Eric&amp;quot;, []),&lt;br /&gt;
                 tree(&amp;quot;Mark&amp;quot;, []),&lt;br /&gt;
                 tree(&amp;quot;Leonard&amp;quot;, []) ] ),&lt;br /&gt;
             tree(&amp;quot;Ellen&amp;quot;, [])&lt;br /&gt;
            ]&lt;br /&gt;
        )).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Заключение=&lt;br /&gt;
&lt;br /&gt;
В этом руководстве были рассмотрены важные понятия:&lt;br /&gt;
&lt;br /&gt;
*Программа на Visual Prolog может содержать множество типов значений: простых и составных, встроенных (стандартных) и определяемых в программе.&lt;br /&gt;
*Простые значения - это: числа, строки и т.д..&lt;br /&gt;
*&amp;#039;&amp;#039;Составные домены&amp;#039;&amp;#039; позволяют рассматривать структуру информации как единую сущность. Составной домен состоит из имени (известного как функтор) и одного или более аргументов. Можно определить домен с несколькими альтернативными фукторами.&lt;br /&gt;
*Функтор в Visual Prolog не есть то же, что функция в других языках программирования. &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Функтор не вызывает никаких вычислений.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; Это просто имя которое обозначает  составной домен и объединяет его аргументы.&lt;br /&gt;
*Составные значения могут обрабатываться и рассматриваться как одиночные переменные; функторы используются для создания различия между различными типами составных значений. Visual Prolog позволяет строить многоуровневые структурированные значения; Аргументами составных доменов могут быть опять-таки составные значения. Используя декларации составных смешанных доменов, можно использовать предикаты, которые:&lt;br /&gt;
**принимают аргументы нескольких различных типов (определяется объявлениями функторов).&lt;br /&gt;
**принимают переменное число аргументов, каждый определенного типа (объявления списков).&lt;br /&gt;
**принимают переменное число аргументов, часть из которых могут иметь более одного типа.&lt;br /&gt;
&lt;br /&gt;
=Ссылки=&lt;br /&gt;
[[en:Compound and List Domains]]&lt;br /&gt;
[[Категория:VipРуководства]]&lt;br /&gt;
[[Категория:VipLanguage]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
	<entry>
		<id>https://wikiru.visual-prolog.com/index.php?title=IDE:_%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5&amp;diff=1017</id>
		<title>IDE: Переменные</title>
		<link rel="alternate" type="text/html" href="https://wikiru.visual-prolog.com/index.php?title=IDE:_%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5&amp;diff=1017"/>
		<updated>2007-11-05T11:16:52Z</updated>

		<summary type="html">&lt;p&gt;SergeMukhin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В IDE Вы можете определить так называемые переменные среды IDE. Мы покажем здесь один из случаев их использования. &lt;br /&gt;
&lt;br /&gt;
Большинство программистов, групп и компаний создают наборы программных пакетов, которые используются во многих проектах. Иногда эти пакеты просто копируются из одного проекта в другой, однако это приводит к появлению большого числа копий одного и того же (почти) программного обеспечения. И каждый раз, найдя ошибку, или улучшив пакет, приходится вносить изменения во все копии использованных таким образом пакетов. &lt;br /&gt;
&lt;br /&gt;
В конце концов Вы решаете сделать эти пакеты разделяемыми вместо их копирования. То есть у Вас будет всего одна копия этих пакетов на диске. Это значит, что пакеты будут теперь лежать вне проектов, вместо того, чтобы лежать внутри их. &lt;br /&gt;
&lt;br /&gt;
Скоро Вы обнаружите, что ссылки на эти пакеты по их абсолютным путям (как c:\sharedTools\tool1\tool1.pack) - плохая практика, поскольку это закрепляет местоположение на диске, а это, в свою очередь затрудняет реструктуризацию дискового пространства, а при работе в группе все члены группы вынуждены использовать одинаковую организационную структуру диска. &lt;br /&gt;
&lt;br /&gt;
Вместо этого Вы можете использовать относительные маршруты (например ..\sharedTools\tool1\tool1.pack), это несколько лучше, поскольку теперь Вы можете установить по крайней мере свою собственную директорию верхнего уровня (и, следовательно, другой диск при необходимости). &lt;br /&gt;
&lt;br /&gt;
Однако по-прежнему расположение пакетов привязано относительно проектов, и это создаёт примерно те же проблемы, что и раньше, только может быть менее жёсткие. &lt;br /&gt;
&lt;br /&gt;
Использование переменных среды IDE повышает гибкость. Давайте рассмотрим имя файла $(Tools)\tool1\tool1.pack. Чтобы его можно было использовать, Вы должны определить переменную среды  $(Tools) и затем Вам необходимо включить переменную $(Tools) в качестве include-директории в установках проекта. &lt;br /&gt;
&lt;br /&gt;
Когда include-маршруты используют переменные IDE, проектное дерево также показывает это. &lt;br /&gt;
&lt;br /&gt;
Теперь, если Вы решите переместить sharedTools в другое место, Вы можете смело это сделать и только переопределить переменную $(Tools). Переменные IDE разделяются среди всех проектов, и такое изменение должно быть сделано лишь один раз для того, чтобы все проекты смогли бы использовать такие пакеты. Разные члены группы могут иметь директории с общими пакетами в различных местах, поскольку каждый член группы использует свою собственную переменную $(Tools). &lt;br /&gt;
&lt;br /&gt;
Такая стратегия используется для $(ProDir), которая всегда ссылается на директорию установки системы программирования Visual Prolog на данном компьютере.&lt;br /&gt;
&lt;br /&gt;
=Ссылки=&lt;br /&gt;
[[en:IDE Variables]]&lt;br /&gt;
[[Категория:VipIDE]]&lt;/div&gt;</summary>
		<author><name>SergeMukhin</name></author>
	</entry>
</feed>