У нас: 141825 рефератів
Щойно додані Реферати Тор 100
Скористайтеся пошуком, наприклад Реферат        Грубий пошук Точний пошук
Вхід в абонемент


зразком cursor (o,o). Змінні вільні, бо зустрічаються вперше. Аналізатор потоку взнає також, що, якщо змінна не використовується в голові фрази, тоді ця змінна буде вихідним аргументом в першому предикатному виклику в тілі фрази.

У виразі R1=R+1 аналізатор потоку встановлює, що змінна R прив'язана, тому що з'являється з предикату cursor. Якщо б вона була вільною, тоді б видавалось повідомлення про помилку. R1 стане після цього виклику відомим аргументом.

В останньому виклику cursor змінні R1 і С зустрічались попередньо, тому вони обробляються як аргументи входу, а виклик буде мати зразок потоку cursor (i,i).

Коли викликається предикат, визначений користувачем, тоді він обробляється аналогічно стандартному предикату. Наприклад,

predicates

changeattribute(Integer, Integer)

clauses

changeattribute(NewAttrib, OldAttrib):-

attribute(OldAttrib), attribute(NewAttrib).

goal

changeattribute(112, Old), write("Hello"),

attribute(Old), write("there").

В розділі goal перший виклик предикату changeattrib зроблено потоковим зразком типу changeattrib(i,o) (тому що перший аргумент відомий - 112, а другий Old - ні). Звідки, в фразі для changeattrib змінна NewAttrib буде аргументом входу, а OldAttrib - аргументом виходу. Але, якщо потоковий аналізатор зустрічає першу підціль attribute(OldAttrib), тоді предикат attribute буде викликатись потоковим зразком attribute (o), а другий виклик attribute буде мати потоковий зразок attribute(i). Кінцевий виклик attribute в goal будет мати потоковий зразок входу, тому що Old з'являється із changeattrib.

10.2.Керування потоковим аналізом.

Якщо потоковий аналізатор взнає, що стандартний предикат викликається не існуючим потоковим зразком, тоді видається повідомлення про помилку. Це може допомогти ідентифікувати нісенітні потокові зразки при створенні предикатів користувача, які використовують стандартні предикати. Наприклад, якщо ви використовуєте предикат

Z = X + Y

де змінна Х або Y не зв'язана, тоді потоковий анализатор видасть повідомлення про помилку: не існує потокового зразка для цього предикату. Для керування такою ситуацією ми можемо використовувати стандартні предикати free і bound.

Припустимо, ми хочемо написати предикат plus для реалізації дії додавання при любих можливих потокових зразках. Програма зображена на малюнку 10.1 реалізує такий предикат.

predicates

plus(integer, integer, integer)

num(integer)

clauses

plus(X,Y,Z) :- bound(X), bound(Y), Z=X+Y. /* (i,i,o) */

plus(X,Y,Z) :- bound(Y), bound(Z), X=Z-Y. /* (o,i,i) */

plus(X,Y,Z) :- bound(X), bound(Z), Y=Z-X. /* (i,o,i) */

plus(X,Y,Z) :- free(X), free(Y),

bound(Z), num(X), Y=Z-X. /* (o,o,i) */

plus(X,Y,Z) :- free(X), free(Z),

bound(Y), num(X), Z=X+Y. /* (o,i,o) */

plus(X,Y,Z) :- free(Y), free(Z),

bound(X), num(Y), Z=X+Y. /* (i,o,o) */

plus(X,Y,Z) :- free(X), free(Y),free(Z),

num(X), num(Y),Z=X+Y. /* (o,o,o) */

/* Генератор чисел, які починаються з нуля */

num(0).

num(X) :- num(A), X = A+1.

Мал.10.1.

10.3. Стиль програмування.

В цьому розділі ми дамо рекомендації для написання елегантних і ефективних програм на Пролозі.

Для виміру ефективності програми традиційно використовують два параметри: пам`ять і час. В Пролозі на покращання цих оцінок значною мірою впливає відміна хвостової рекурсії.

Розглянемо відомий нам предикат member:

member (X, [X|_]).

member (X,[_|Y]): member (X,Y)

Ітеративна операція перевірки або генерації елементів потрібного списку проводиться рекурсивним чином, тому стекова пам`ять (а відповідно і час виконання) повністю залежить від рекурсії. Припустимо, що предикат demopred описується так:

demopred (X,Y): - ... , member (A,B), test (A), ...

При активізації member спочатку система повинна запам`ятати, що після успішного виконання member керування потрібно передати предикату test. Тому, адреса test повинна бути збережена в стеці. Для кожного рекурсивного звертання до member система запам`ятовує адресу, до якої повинен повернутись предикат member після успішного завертання цього предикату. Враховуючи, що між member (X,[_,Y]):- і рекурсивним зверненням member(X,Y) не має точок повернення до попереднього стану, не має необхідності зберігати адресу member в стеці декілька разів. Достатньо запам`ятати, що після успішного завершення предикату member керування повинно бути передане предикату test. Це і буде відміною хвостової рекурсії. Там, де система не може відмінити рекурсію, програміст може зробити це сам, використовуючи наступні правила.

Правило1. Краще використовувати більше змінних, ніж предикатів.

Це правило знаходиться часто в прямому конфлікті з наглядністю програми. В багатьох випадках декларативний стиль Прологу приводить до гіршого коду в порівнянні з процедурними мовами. Наприклад, якщо ви пишете предикат, для перестановки елементів списку, тоді такий фрагмент коду, як:

reverse(X,Y):- reverse1([],X,Y). /*More efficient*/

reverse1(Y, [], X).

reverse1(X1, [U | X2], Y):- reverse1([U|X1],X2,Y).

пред'являє менше вимог до стеку, ніж використання додатково предикату append:

reverse([],[]).

reverse([U | X], Y):- reverse(X,Y1), append(Y1,[U],Y).

append([],Y,Y).

append([U|X],Y,[U|Z]):-append(X,Y,Z).

Правило 2. При впорядкуванні підцілей в правилі, першими розміщуйте підцілі з найбільш зв'язаними змінними.

Наприклад ви пишете предикат для вирішення системи рівнянь

Х + 1 = 4

Х + Y = 5

використовуючи метод "генеруй_ і_перевіряй":

solve(X,Y):- /*кращій варіант*/

num(X), plus(X, 1, 4),

num(Y), plus(X, Y, 5).

Тоді він буде кращим за наступний фрагмент :

solve(X,Y):- /*гірший варіант*/

num(X), num(Y),

plus(X, Y, 5), plus(X, 1, 4).

Предикати num і plus визначались нами раніше.

Правило 3. Коли не існує рішень, пробуйте перевіряти, що виконання видасть повідомлення про "неуспіх" эффективно.

Припустимо, що ми хочемо написати предикат singlepeak, який перевіряє наявність "піку" серед списку цілих чисел. Іншими словами, числа повинні зростати до одного максимуму, а потім спадати. Для такого предикату виклик

singlepeak ([1,2,5,7,11,8,6,4]).

буде успішним, тоді як виклик

singlepeak ([1,2,3,9,6,8,5,4,3]).

видасть повідомлення "неуспіх".

Наступне визначення для singlepeak не враховує Правило 3, тому що наявність в списку однієї "вершини" проводиться тільки у випадку, коли append розбиває список на різноманітні декомпозиції:

singlepeak(X):- append(X1,X2,X), up(X1), down(X2).

up(_).

up([U,V|Y]):- U < V, up([V,Y]).

down([]).

down([U]).

down([U,V|Y]):- U > V, down ([V|Y]).

Можна також запропонувати варіанти, які враховують настанови Правила3:

singlepeak([]).

singlepeak([U,V|Y]):- U < V, singlepeak([U|Y]).

singlepeak([U,V|Y]):- U > V, down([V|Y]).

down([]).

down([U]).

down([U,V|Y]):- U < V, down ([V|Y]).

або ж варіант:

singlepeak([],_).

singlepeak([U,V|W],up):- U < V, singlepeak([V|W],Up).

singlepeak([U,V|W],_):- U


Сторінки: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22