Метапрограммирование
ВВЕДЕНИЕ
Пролог - это язык, предназначенный для поиска решений. Это декларативный язык, то есть формальное определение (постановка) задачи может быть использовано для ее решения. Пролог определяет логические отношения в задаче, как отличные от пошагового решения этой задачи.
Центральной частью Пролога являются средства логического вывода, которые решают запросы, используя заданное множество фактов и правил, к которым обращаются как к утверждениям. Пролог также не имеет деления переменных на типы и может динамически добавлять правила и факты к средствам вывода. Таким образом, это гибкий язык, и он более пригоден для объектно-ориентированного расширения, чем язык со строго заданными типами, например, Паскаль.
Благодаря наличию в языке Prolog возможностей по манипулированию символами он является мощным средством реализации других языков и принципов программирования.
Метапрограммирование — вид программирования, связанный с созданием программ, которые порождают другие программы как результат своей работы (в частности, на стадии компиляции их исходного кода), либо программ, которые меняют себя во время выполнения (самомодифицирующийся код).
В данной работе показано, как можно создать метаинтерпретаторы Prolog — интерпретаторы языка Prolog на самом языке Prolog. Здесь рассматривается специальный метод компиляции программ, называемый обобщением на основе объяснения, который был разработан как один из подходов к машинному обучению. Кроме того, описаны простые интерпретаторы для двух других подходов к программированию: объектно-ориентированное программирование и программиро- вание, управляемое шаблонами.
1 МЕТАПРОГРАММЫ И МЕТАПРОГРАММИРОВАНИЕ
Meтапрограммой называется программа, которая принимает в качестве данных другие программы. Примерами метапрограмм являются интерпретаторы и компиляторы. Особой разновидностью метапрограмм являются метаинтерпретаторы — интерпретаторы для некоторого языка, написанные на том же языке. Таким образом, метаинтерпретатором Prolog является интерпретатор языка Prolog, который сам написан на языке Prolog.
Благодаря наличию в языке Prolog возможностей по манипулированию символами он является мощным языком, который может успешно применяться для метапрограммирования. Поэтому он часто служит в качестве языка реализации для других языков. Многие специалисты отмечают, что Prolog особенно хорошо подходит для использования: в качестве языка быстрой разработки прототипов в тех ситуациях, когда требуется как можно быстрее воплотить в жизнь новые идеи. Это особенно важно в тех случаях, когда возникает необходимость разработать новый язык, осуществить на практике новый принцип программирования или опробовать вновь созданную архитектуру программы. Этот язык позволяет быстро реализовывать новые идеи и проводить эксперименты. При разработке прототипов основное внимание уделяется тому, чтобы новые идеи оформлялись в виде экспериментального образца максимально быстро и с наименьшими затратами, благодаря чему можно было бынемедленно приступать к их проверке. С другой стороны, особое значение повыше- нию эффективности такой реализации не придается. После проверки первоначального замысла прототип может быть реализован повторно, для чего можно воспользоваться другим, более эффективным языком программирования. И даже если потребуется отказаться от прототипа на языке Prolog в пользу другого языка, все равно усилия, затраченные на разработку этого прототипа, не будут напрасными, поскольку он обычно позволяет ускорить творческий этап разработки.
2 МЕТАИНТЕРПРЕТАТОРЫ Prolog
2.1 Простейший метаинтерпретатор Prolog
Метаинтерпретатор Prolog принимает на входе программу Prolog и цель Prolog, после чего выполняет данную цель применительно к данной программе; это означает, что метаинтерпретатор пытается доказать, что цель логически следует из этой программы. Но для того чтобы он имел определенное практическое значение, метаинтерпретатор не должен действовать полностью аналогично оригинальному интерпретатору Prolog; от него требуются некоторые дополнительные функциональные воз- можности, такие как формирование дерева доказательства или трассировка выполнения программ.
В этой главе для упрощения предполагается, что данная программа уже использовалась для консультации системой Prolog, которая вызывает на выполнение метаинтерпретатор. Поэтому метаинтерпретатор может быть определен как процедура prove с одним параметром (таковым является цель, которая должна быть достигнута) следующим образом:
prove( Goal) Как показано ниже, простейший метаинтерпретатор Prolog является тривиальным.
prove [ GoalJ :- call( Goal] .
В данном случае вся работа метаинтерпретатора была передана (с помощью предиката call) оригинальному интерпретатору Prolog, поэтому такой метаинтерпретатор ведет себя точно так же, как и сам интерпретатор Prolog. Безусловно, что такой вариант метаинтерпретатора не имеет никакого практического значения, поскольку он не предоставляет какие-либо дополнительные возможности. Для того чтобы обеспечить реализацию таких важных средств метаинтерпретатора, как формирование деревьев доказательства, прежде всего необходимо уменьшить "степень детализации" этого интерпретатора, чтобы он мог обрабатывать не только всю программу, но и ее компоненты. Такое уменьшение степени детализации метаинтерпретатора становится возможным благодаря наличию следующего встроенного предиката, предусмотренного во многих реализациях Prolog:
c l a u s e ( Head, Body)
Этот предикат осуществляет "выборку" любого предложения из программы, применяемой для консультации. Здесь Head — голова предложения, извлеченного из базы данных, a Body — его тело. Для единичного предложения (факта) параметр Body = t r u e . В неединичном предложении (правиле) тело может содержать одну или несколько целей. Если оно содержит одну цель, то Body и есть эта цель. Если тело содержит несколько целей, то их выборка осуществляется в виде следующей пары: Body = ( FirstGoal, OtherGoals)
В этом терме запятая представляет собой встроенный инфиксный оператор. В стандартной системе обозначений Prolog эту пару можно заменить следующим эквивалентным термом: ,( FirstGoal, OtherGoals} где OtherGoals может в свою очередь представлять собой пару, состоящую еще из одной цели и оставшихся целей. В вызове c l a u s e {Head, Body) первый параметр Head не должен быть переменной. Предположим, что программа, применяемая для консультации, содержит обычную процедуру member. В таком случае выборка предложений процедуры member может быть выполнена таким образом:
?- clause( member(X, L), Body). X =_14 L = \JLA | _15] Body = true;
X = _14 L = [_15 I _16] Body = member{ _14, _16)
В листинге 1.1 показан простой метаинтерпретатор для языка Prolog, реализованный с такой степенью детализации, которая, как показала практика, является удобной для большинства областей применения. Но следует отметить, что этот метаинтерпретатор предназначен только для так называемого "чистого" (базового) языка Prolog. Он не позволяет обрабатывать встроенные предикаты, в частности оператор отсечения. Но практическая применимость этого простого метаинтерпретатора опре- деляется тем фактом, что он предоставляет схему, которая может быть легко модифицирована для получения других интересных эффектов. Одним из подобных широко известных расширений является создание средства трассировки для системы Prolog. Еще одна из его полезных особенностей состоит в том, что с его помощью можно предотвратить вхождение интерпретатора Prolog в бесконечные циклы путем ограничения глубины вызовов подцелей.
Листинг 2.1.1, Простой метаинтерпретатор Prolog
2.2 Трассировка с помощью метаинтерпретатора
Ниже приведен результат первой попытки расширить простой метаинтерпретатор, приведенный в листинге 2.1.1, чтобы создать интерпретатор, обеспечивающий трассировку.
В данном случае операторы отсечения требуются для предотвращения необходимости отображать "истинные" (true) и составные цели в форме ( Goall, Goal2). Эта программа трассировки имеет несколько недостатков: она не обеспечивает трассировку недостигнутых целей и не показывает результатов перебора с возвратами, когда повторно выполняется одна и та же цель. В этом отношении программа трассировки, приведенная в листинге 2.2.1, является более совершенной. Кроме того, для удобства чтения отображаемые цели обозначаются в ней отступами, пропорционально той глубине логического вывода, на которой они вызываются. Но и этот метаин- терпретатор все еще ограничивается чистым языком Prolog. Пример вызова рассматриваемой программы трассировки приведен ниже.
?- trace ( ( member! У-, I a, b]) , member! X, [ Ь, с] ) ) ) .
Call: ir.ember( _00B5, [a, b] )
Call: member( a, t b, c])
Call: member(a, [ c]}
Call: member[a, [ ] }
Fail: member[a, [])
Fail: member(a, [ c])
Fail: member[ a, [ b, c])
Redo: member( a, [ a, b])
Call: member! _00S5, [ b))
Exit: member[ b, [ b])
Exit: member( b, | a, b]]
Call: member! b, [ b, с]]
Exit: member! b, [ b, c])
Ъ trace! Goal): выполнить цель Goal, которая определена в программе Prolog, к и вывести информацию трассировки
trace(Goal) :-
trace! Goal, 0) .
trace( true, Depth) :- !. % Красный оператор отсечения; Depth - глубина вызова
trace( ( Goall, Goal2), Depth) :- !,
trace! Goall, Depth;, % Красный оператор отсечения
trace! Goal2, Depth).
trace] Goal, Depth; :-
display! 'Call: ', Goal, Depth),
clause! Goal, Body),
Depthl is Depth + 1,
trace( Body, Depthl),
display! 'Exit: ', Goal, Depth),
display_redo( Goal, Depth).
t r a c e ! Goal, Depth) :- % Бее альтернативные варианты исчерпаны
display! 'Fail: ', Goal, Depth),
fail.
display! Message, Goal, Depth) :-
tab( Depth), writet Message),
write( Goal), nl.
display_redo( Goal, Depth) :-
true
display! 'Redo: ', Goal, Depth), % Затем вывести сообценме о том,
% что начинается переоор с возвратами,
fail. % и вынудить систему выполнить перебор
% с возвратами
Эта программа трассировки выводит описанную ниже информацию для каждой выполняемой цели.
1. Цель, которая должка быть выполнена (Call: Goal).
2. Трассировка подцелей (с отступами).
3. Если цель достигается, то отображается ее окончательная конкретизация (Exit: Instar.tiatedGoal); если цель не достигается, то отображается результат F a i l : Goal.
4. В случае перебора с возвратами к ранее достигнутым целям сообщение принимает вид Redo: InstantiatedGoal (конкретизация этой цели в предыдущем решении).
Безусловно, существует
возможность продолжить доработку
этого трассирующего
2.3 Формирование деревьев
Еще одним широко известным расширением простого интерпретатора, приведенного в листинге 1.1, является формирование деревьев доказательства. Поэтому после достижения некоторой цели вырабатывается дерево ее доказательства, которое может служить для дальнейшей обработки. Формирование деревьев доказательства реализовано для экспертных систем на основе правил. Хотя синтаксис этих правил отличается от синтаксиса Prolog, принципы формирования дерева доказательства остаются одинаковыми. Эти принципы можно легко реализовать в метаинтерпретаторе, приведенном в листинге 1.1. Например, предположим, что можно представить дерево доказательства в зависимости от конкретного случая, как описано ниже.
1. Для цели t r u e деревом доказательства
2. Для пары целей [ Goall, Goal2) деревом доказательства является пара ( Proof I, Proof 2), состоящая из деревьев доказательства этих двух целей.
3. Для цели Goal, согласующейся с головой предложения, телом которого является Body, деревом доказательства служит Goal <= Proof, где Proof — дерево доказательства Body.
Эти требования можно реализовать в простом метаинтерпретаторе, приведенном в листинге 2.1.1, следующим образом:
:- ор( 500, xfy, <==).
prove[ true, true).
prove{ ( Goall, Goal2), ( Proofl, Proof2)) :-
prove( Goall, Proofl),
prove ( Goal2, Proof2) .
prove( Goal, Goal <== Proofl :-
clause( Goal, Body),
prove[ Body, Proof),
Такое дерево доказательства может использоваться различными способами: в качестве основы для выработки объяснения последовательности рассуждений в экспертной системе. В следующем разделе рассматривается еще один интересный вариант использования дерева доказательства — обобщение на основе объяснения.
3 ОБОБЩЕНИЕ НА ОСНОВЕ ОБЪЯСНЕНИЯ
Идея обобщения
на основе объяснения впервые была реализована
в области машинного обучения, целью которого
является обобщение заданных примеров
в виде общих описаний рассматриваемых
понятий. Одним из способов формирования
таких описаний является обобщение
Способ EBG основан на использовании при формировании обобщенных описаний следующей идеи: если дан некоторый экземпляр целевого понятия, то можно воспользоваться теорией проблемной области для объяснения того, как этот экземпляр фактически удовлетворяет данному понятию. Затем анализируется это объяснение и предпринимается попытка обобщить его таким образом, чтобы оно подходило не только для заданного экземпляра, но и для множества "аналогичных" экземпляров. Затем такое обобщенное объяснение становится частью описания рассматриваемого понятия и может в дальнейшем использоваться при распознавании экземпляров этого понятия. Кроме того, требуется, чтобы формируемое описание понятия было операционным; это означает, что оно должно быть сформулировано в терминах понятий, объявленных пользователем как операционные. Интуитивно можно себе представить, что описание понятия является операционным (определяющим некоторую операцию), если его можно (относительно) легко использовать на практике. Задача определения того, что подразумевается под словом "операционный", полностью возлагает- ся на пользователя.
В реализации метода EBG эти абстрактные идеи должны были стать более конкретными. Один из способов их реализации в логике языка Prolog состоит в следующем.
• Понятие реализуется в качестве предиката,
• Описание понятия представляет собой определение предиката.
• Объяснением является дерево доказательства, которое демонстрирует, как заданный экземпляр удовлетворяет целевому понятию.
• Теория проблемной области представлена в виде множества доступных предикатов, определенных как программа Prolog.
В таком случае задача обобщения на основе объяснения может быть сформулирована, как описано ниже.
Дано
• Теория проблемной области. Множество предикатов, доступное для механизма обобщения на основе объяснения, включая целевой предикат, для которого должно быть сформировано операционное определение.
• Критерии операционности. Эти критерии определяют предикаты, которые могут использоваться в определении целевого предиката.
• Учебный пример. Множество фактов, описывающих конкретную ситуацию и экземпляр целевого понятия таким образом, чтобы этот экземпляр мог быть получен путем логического вывода из заданного множества фактов и теории проблемной области.
Найти
- Обобщение учебного экземпляра и операционное определение целевого понятия; это определение состоит из достаточного условия (в терминах операционных предикатов) для данного обобщенного экземпляра, при котором удовлетворяется целевое понятие.
Метод обобщения на основе объяснения в такой постановке может рассматриваться как своего рода компиляция программы с преобразованием из одной формы в другую. Первоначальная программа определяет целевое понятие в терминах предикатов теории проблемной области. Откомпилированная программа определяет то же целевое понятие (или подпонятие) в терминах "целевого языка", т.е. только с помощью операционных предикатов. Механизм компиляции, предусмотренный в методе EBG, является довольно необычным. Он предусматривает выполнение первоначальной программы на заданном примере, что приводит к получению дерева доказательства. Затем это дерево доказательства обобщается таким образом, что сохраняется его структура, но постоянные заменяются переменными при любой возможности. В полученном таким образом обобщенном дереве доказательства некоторые узлы указывают на операционные предикаты. Затем дерево сокращается так, что остаются только эти "операционные узлы" и корень. Полученный результат представляет собой операционное определение целевого понятия.
Это описание проще всего проиллюстрировать на примере практического применения метода EBG. В листинге 3.1 приведены два определения теорий проблемной области для EBG. Первая теория проблемной области касается вручения подарка, а вторая описывает движения лифта. Рассмотрим первую проблемную область. Допустим, что учебный экземпляр состоит в следующем:
% Джон вручает сам себе шоколад (разрешает себе его съесть) gives( John, John, chocolate)
Листинг 3.1. Два определения задач для обобщения на основе объяснения
% Для совместимости с некоторыми реализациями Prolog следующие предикаты % определены как динамические:
:- dynamic gives/3, would_please/2, would_comfort/2, feels_sorry_for/2,
go/3, move/2, move_list/2.
$ теория проблемной области, которая относится к теме вручения подарков
gives( Personl, ?erson2, Gift) :-
likes( Personl, Person2),
would_please( Gift, Person21.
gives( Personl, Persan2, Gift; :-
feels_sorry_for( Personl, Person2),
would_coinfort [ Gift, Person2) .
would_please( Gift, Person) :-
neecis( Person, Gift).
would_comfort( Gift, Person) :-
likes[ Person, Gift).
feels_sorry_for{ Personl, Person2) :-
likes ( Personl, Perso:i2) ,
sad{ Person2).
feels_sorry_for( Person, Person) :-
sad( Person;.
i Операционные предикаты
operational{ likes( _, _)).
operational( needs( _, _)) .
operati.cr.al ( sad [ _) ) .
II Пример проблемной ситуация
likes( John, annie).
likes( annie, John).
likes! John, chocolate;,
needs( annie, tennis_racket).
sad{ John).
% Еще одна теория проблемной области, применяемая для описания движений .лифта
i go( Level, GoalLevel, Moves) если список движений Moves перевозит лифт с этажа Level на этаж GoalLevel
go! Level, GoalLevel, Moves) :-
move_list( Moves, Distance!, % Список движений и пройденное расстояние
Distance -:- GoalLevel - Level.
move_list( [] , 0) .
move_list( [Movel | Moves], Distance + Distance1) :-
move_list( Moves, Distance),
move( Movel, Distancel).
move[ up, 1) .
move( down, -1) .
operational( A =:= B) .
В результате выработки доказательства рассматриваемый метаинтерпретатор находит следующее доказательство обоснованности выполненного действия:
gives ( John., John, chocolate) <==
( feels_sorry_for{ John, John) <== sad( John), % Джону стало жалко самого себя,
% поскольку ему грустно,
would_comfort{ chocolate, John) <== likes( John, chocolate) % и Джону будет $ приятно, поскольку он любит шоколад
Это доказательство может быть обобщено путем замены постоянных John иchocolate переменными следующим образом:
gives( Person, Person, Thing! <== % Человек вручает себе какую-то вещь, если ( feels_soi:ry_for( Person, Person) <-- sad( Person), I человеку жалко себя,
% потому что ему грустно,
would^comfort( Thing, Person! <== likes[ Person, Thing) % и человеку будет
% приятно, поскольку ему нравится эта вешь
)
В листинге 3.1 предикаты sad (грустит) и likes (любит) определены как операционные. Теперь может быть получено операционное определение предиката gives путем удаления из дерева доказательства всех узлов, кроме узлов, указанных как "операционные", и корневого узла. Это приводит к получению такого результата:
Таким образом, достаточное условие Condition для предиката gives ( Person, Person, Thing) состоит в следующем:
Condition « ( sad( Person), likes( Person, Thing))
Теперь это новое определение можно ввести в первоначальную программу, как показано ниже.
asserta ( ( gives( Person, Person, Thing) :- Condition))
В результате получено следующее новое предложение, касающееся предиката gives, для которого требуется оценка только операционных предикатов:
gives( Person, Person, Thing) :-
sad( Person) ,
likes( Person, Thing).
Благодаря обобщению заданного экземпляра gives { John, John, chocolate) было сформировано определение (в операционных терминах) как один из общих случаев применения предиката gives для описания одного из способов самоутешения.
Проблемная область с описанием движений лифта, приведенная в листинге 3.1, является немного более сложной, поэтому проведем с ней эксперименты после реализации метода EBG на языке Prolog. !!!Метод EBG можно запрограммировать как двухэтапный процесс: на первом этапе вырабатывается дерево доказательства для данного примера, а на втором это дерево доказательства обобщается и из него извлекаются "операционные узлы". Для этой цели можно применить описанный выше метаинтерпретатор, который формирует деревья доказательства. Но фактически эти два этапа не требуются. Более удобный способ состоит в том, чтобы откорректировать простой метаинтерпретатор, приведенный в листинге 3.1, таким образом, чтобы операции обобщения чередовались с операциями доказательства заданного экземпляра. Модифицированный подобным образом метаинтерпретатор, который реализует рассматриваемый метод EBG, будет именоваться ebg и теперь будет иметь три параметра:
ebg( Goal, GenGoal, Condition)
где Goal — заданный пример, который должен быть доказан, GenGoal — обобщен- ная цель. Condition — полученное путем логического вывода достаточное условие для GenGoal, сформулированное в терминах операционных предикатов. Такой обобщающий метаинтерпретатор приведен в листинге 3.2. Для проблемной области с описанием процесса получения подарков {см. листинг 3.1) метаинтерпретатор ebg может быть вызван следующим образом:
?- ebg( gives! John, John, chocolate), gives! X, Y, Z) , Condition].
x=Y
Condition - ( sacl( X), likes( X, Z))
Листинг 3.2. Программа обобщения на основе объяснения
Теперь попытаемся исследовать проблемную область с описанием движений лифта. Предположим, что цель, которая должна быть решена и обобщена, состоит в определении последовательности движений Moves, которые переводят лифт с третьего этажа на шестой, как показано ниже. до( 3, 6, Moves)
Теперь можно вызвать на выполнение программу ebg и получить результирующую обобщенную цель и ее условие следующим образом:
Результирующее новое предложение, касающееся предиката до, состоит в следующем:
go( Leveil, Level2, [ up. Up, up] ) :-
0 + 1 + 1 + 1 =-:- Level2 - Level 1.
С помощью метода EBG простая операция перемещения лифта вверх на три этажа была обобщена как операция перемещения вверх между любыми двумя этажами, находящимися друг от друга на расстоянии в три этажа. Чтобы решить задачу по достижению цели до ( 3, 6, Moves), первоначальная программа выполняет поиск среди последовательностей действий по подъему (up) и спуску (down). А с помощью вновь выведенного предложения задача перемещения вверх с одного этажа на другой на расстояние в три этажа (например, до( 7, 10, Moves}) решается немедленно, без какого-либо поиска.
Метаинтерпретатор ebg, приведенный в листинге 3.2, снова является одной из производных простого метаинтерпретатора, показанного в листинге 2.1. Этот новый метаинтерпретатор вызывает встроенную процедуру copy_term. Вызов этой процедуры в форме
copy_term{ Term, Copy)
позволяет сформировать для заданного терма Term его копию Сору с переименованными переменными. Это удобно, если требуется сохранить терм в его первоначальном виде и в то же время обеспечить его обработку таким образом, чтобы переменные этого терма могли стать конкретизированными. При такой обработке можно использовать копию терма, притом что переменные в первоначальном терме остаются незатронутыми.
Последнее предложение в процедуре ebg заслуживает дополнительного поясне- ния. Вызов c l a u s e ! GenGoal, GenBody) обеспечивает выборку предложения, которое может использоваться для доказательства обобщенной цели. Это означает, что метаинтерпретатор фактически предпринимает попытку доказать обобщенную цель. Но следующая строка налагает некоторое ограничение на эту операцию:
copy_term( ( GenGoal, GenBody), I Goal, Body))
В ней предъявляется требование, чтобы переменные GenGoal и Goal были согласованными. Согласование выполняется над копией GenGoai, поэтому переменные в этой общей цели остаются незатронутыми. Причина, по которой требуется такое согласование, состоит в том, что выполнение обобщенной цели должно ограничиваться такими альтернативными ветвями дерева доказательства (предложениями), которые являются применимыми для заданного примера (Goal). Благодаря этому выполнением обобщенной цели управляет сам пример. Подобное управление и составляет суть компиляции программы в стиле EBG. Без такого управления возникает вероятность того, что будет найдено такое доказательство для GenGoal, которое не применимо для Goal. В подобных случаях обобщение полностью не соответствует примеру.
После того как пример обобщен и сформулирован в терминах операционных предикатов, он может быть добавлен в виде нового предложения к программе для использования при поиске ответов на будущие подобные вопросы путем оценки только операционных предикатов. Таким образом, метод компиляции KBG преобразует программу в "операционный" язык. Откомпилированная программа может выполняться интерпретатором, который "знает" только данный операционный язык. Одним из возможных преимуществ этого может стать создание более эффективной программы. Ее эффективность может быть повышена в следующих двух направлениях: во- первых, обработка операционных предикатов может осуществляться проще по сравнению с другими предикатами, и, во-вторых, последовательность обработки предика- тов, показанная на примере, может оказаться более подходящей по сравнению с той, которая была предусмотрена в первоначальной программе, поскольку в откомпилированном определении вообще не появляются ветви, которые приводят к неудачному завершению. При компиляции программы с помощью метода EBG необходимо следить за тем, чтобы новые предложения не вступали в противоречие с первоначаль- ными предложениями неконтролируемым способом.
4 ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ
В системе Prolog можно
легко реализовать объектно-
Чтобы ознакомиться с тем, как этот принцип практически осуществляется в системе Prolog, рассмотрим в качестве примера объектно-ориентированную программу, касающуюся геометрических фигур. Одним из объектов этой программы яв- ляется прямоугольник, а в состав данного объекта могут входить процедуры для описания прямоугольника и вычисления его площади. Если объекту прямоугольника передается сообщение area ( А) , он отвечает, вычисляя площадь прямоугольника, и переменная А становится конкретизированной значением этой площади. В рассмат- риваемой реализации объект будет представлен как отношение object[ Object, Methods)

- Метасоматизм, его проблемы и роль в геологической историии
- Мета створення ОСББ, правові та організаційн і засади
- Метастронгилез свиней
- Мета та завдання технічного обслуговування та ремонту
- Мета та зміст навчання аудіювання учнів середніх класів
- Мета та основні завдання аудиту ефективності
- Мета та основні напрямки маркетингової діяльності
- Металургійний комплекс України
- Металургійний комплекс України: регіональні особливості розвитку та розміщення
- Метаморфизм
- Метановый процесс
- Метаногенные археи
- Мета ознаки необхідної оборони
- Метапоисковая система Internet