Смекни!
smekni.com

Программирование и разработка приложений в Maple (стр. 47 из 135)

> op(4, eval(Proc));

table([

(<Фактические аргументы_1>) = <Возвращаемое значение_1>

============================================== (<Фактические аргументы_h>) = <Возвращаемое значение_h>])

Как следует из представленного, remember-таблица в качестве входов использует последовательности фактических аргументов каждого ее вызова, а выходов - значения, возвращаемые на соответствующих фактических аргументах. Данная организация обеспечивает эффективную работу с часто используемыми или рекурсивными процедурами, ибо при вызове процедуры в случае установления наличия в ее remember-таблице аналогичного вызова, производится возврат соответствующего результата без повторного выполнения тела процедуры, т.е. входы в таблицу не дублируются. Целый ряд средств пакета определены с remember-параметром, например, readlib-функция и др.

Между тем, использование remember-механизма может наряду с повышением эффективности выполнения процедуры существенно использовать ресурсы оперативной памяти ЭВМ, требуемые для размещения remember-таблицы. В этом случае требуется нахождение оптимального консенсуса, обеспечиваемого, в частности, возможностью как полного обнуления таблицы, так и отдельных ее входов. В частности, совместное использование с remember-параметром system-параметра обеспечивает автоматическую очистку (без ее удаления) remember-таблицы в процессе выполнения ядром периодических операций по очистке от “муссора” занимаемой текущим сеансом памяти ПК. Именно поэтому не следует использовать system-параметр для тех процедур, текущий результат выполнения которых зависит от истории вычислений, как это имеет место для рекурсивных процедур. Другие средства Maple-языка позволяют производить модификацию remember-таблицы более дифференцированно.

Для обнуления remember-таблицы Proc-процедуры применяется процедура forget(Proc). Вызов forget(Proc{, А}{, 0}) позволяет удалять из remember-таблицы входы для определяемой первым фактическим аргументом Proc-процедуры. При этом, допускается передавать Proc-процедуре фактические А-аргументы и использовать необязательные две Оопции (reinitialize, subfunction), управляющие непосредственным выполнением forgetпроцедуры. Детальнее с ними можно ознакомиться по справке пакета. Посредством передачи фактических А-аргументов обеспечивается возможность удаления из rememberтаблицы указанной Proc-процедуры ее конкретных Proc(A)-вызовов. Тогда как в случае отсутствия фактических А-аргументов remember-таблица полностью обнуляется, однако оставаясь ассоциированной с данной процедурой. Наконец, посредством выполнения подстановки subsop(4=NULL, eval(Proc)) производится удаление таблицы remember для Proc-процедуры. Однако, forget-процедуру следует использовать с определенной осмотрительностью.

Прежде всего, forget-процедуру не рекомендуется использовать для расширенного управления remember-таблицей процедур, т.к. она, в частности, не работает с целым рядом модульных процедур пакета, а также со многими процедурами, определенными в модулях Share. Ряд достаточно простых примеров нижеприведенного фрагмента иллюстрирует вышесказанное. Для модификации процедурной таблицы можно использовать и нашу процедуру Remember_T от неопределенного числа формальных аргументов, по которой производится редактирование входов и выходов таблицы. Следующий простой фрагмент иллюстрирует текст процедуры [12,103] и примеры ее применения:

Remember_T := proc(NP::{symbol, name}) local k p h G F S, , , , , ; global __T; if not type(NP procedure, ) then ERROR "<%1> is not a procedure",( NP) elif nargs < 2 then RETURN(op 4,( eval(NP))) else assign('__T' = op(4, eval(NP)), F = cat([libname][1][1 .. 2], "&bsol;$$$vgs")) end if;

for k from 2 to nargs do

assign(' 'G = "", ' 'S = "" ;) if whattype args[ ]( k ) = `=`then __T[op lhs(( args[ ])k )] := rhs args[ ]( k ) else for p to nops args[ ]( k ) − 1 do G := cat(G, convert(args[ ]k [p], 'string'), ",") end do;

G := cat "'__T["( , cat(G, convert(args[ ]k [nops args[ ]( k )], 'string')), "]';"); assign(' 'S = convert(__T[args[ ]k ], 'string')), assign(' 'S = cat(S[1 .. 4], S[6 .. -2]));

assign(' 'G = cat(cat(S, ":="), G)), writebytes(F, convert(G, 'bytes')), close(F);

read F; __T[seq(args[ ]k [p], p = 1 .. nops(args[ ])k )] := %, fremove(F) end if

end do;

unassign ' ( __T'), op 4,( eval(NP))

end proc

> P3:=proc() options operator, arrow, remember; evalf(sum(args[k], k=1..nargs)/nargs) end proc: [P3(10,17,39,44,59,64), P3(96,89,67,62,47,42), P3(30,3,99,350,520,5)]: op(4, eval(P3)); table([(96, 89, 67, 62, 47, 42) = 67.16666667, (10, 17, 39, 44, 59, 64) = 38.83333333,

(30, 3, 99, 350, 520, 5) = 167.8333333])

> Remember_T(P3, [10, 17, 39, 44, 59, 64]=2006, [96, 89, 67, 62, 47, 42], [31, 3, 99, 42]=[10, 17]):

> op(4, eval(P3));

table([(10, 17, 39, 44, 59, 64)=2006, (30, 3, 99, 350, 520, 5)=167.8333333, (31, 3, 99, 42)=[10, 17]])

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

[x1, x2, …, xn] = <Значение> либо [x1, x2, …, xn]

где первый формат определяет необходимость замены выхода на (x1, x2, …, xn)-входе таблицы remember на заданное значение или помещение в таблицу нового входа, если указанный отсутствует, либо удаления заданного входа из таблицы. Затем определяется P3-процедура с опцией remember, согласно которой для процедуры создается с remember-таблица. После трех вызовов процедуры содержимое этой таблицы выводится. Последующий вызов Remember_T-процедуры иллюстрирует результат модификации remember-таблицы процедуры P3 в разрезе перечисленных трех операций. Организация Remember_T не рассматривается и оставляется читателю в качестве полезного упражнения.

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

> Proc:= proc() options package; evalf(sum(args[k], k= 1 .. nargs)/nargs) end proc:

> Proc(10, 17, 39, 44, 59, 64); ⇒ 38.83333333

> Proc1:= proc() options trace; evalf(sum(args[k], k= 1 .. nargs)/nargs) end proc:

> Proc1(10, 17, 39, 44, 59, 64);

{--> enter Proc1, args = 10, 17, 39, 44, 59, 64

38.83333333

<-- exit Proc1 (now at top level) = 38.83333333}

38.83333333

> Proc2:= proc() options operator, arrow; evalf(sum(args[k], k= 1 .. nargs)/nargs) end proc: > [eval(Proc2), Proc2(10, 17, 39, 44, 59, 64)];

( ) → evalf nargsk∑ = nargs1 argsk , 38.83333333 

> Proc3:= proc() options operator, arrow, remember; evalf(sum(args[k], k= 1 .. nargs)/nargs) end proc: [Proc3(10, 17, 39, 44, 59, 64), Proc3(96, 89, 67, 62, 47, 42), Proc3(30, 6, 99, 350, 520)]: op(4, eval(Proc3)); table([(96,89,67,62,47,42)=67.1666667, (30,6,99,350,520)=201., (10,17,39,44,59,64)=38.8333333])

> forget(Proc3, 10, 17, 39, 44, 59, 64): op(4, eval(Proc3));

table([(96, 89, 67, 62, 47, 42) = 67.16666667, (30, 6, 99, 350, 520) = 201.]) > Proc_30:= proc() options remember; sum(args[n], n= 1 .. nargs) end proc:

> h:= 0: for k to 300 do h:= h + Proc_30(a$a = 1 .. k) end do: h; ⇒ 4545100

> nops([indices(op(4, eval(Proc_30)))]); ⇒ 300

> Fib:= proc(n::integer) if n = 0 or n = 1 then n else Fib(n - 1) + Fib(n - 2) end if end proc:

> Fib1:= proc(n::integer) option remember; if n = 0 or n = 1 then n else Fib1(n - 1) +

Fib1(n - 2) end if end proc: T:= time(): [Fib(32), time() - T]; ⇒ [2178309, 12.157]

> T:= time(): [Fib1(32), time() - T], nops([indices(op(4, eval(Fib1)))]); ⇒ [2178309, 0.], 33

Между тем, наиболее важной особенностью remember-параметра является возможность на основе remember-таблицы определять эффективные во временном отношении рекурсивные процедуры. Обеспечивая возможность определения рекурсивных процедур, механизм ядра, вместе с тем, определяет эффективность их выполнения в зависимости от использования таблицы remember. В конце предыдущего фрагмента приведены две простые функционально эквивалентные рекурсивные процедуры Fib и Fib1, вычисляющие числа Фибоначчи; при этом, описательная часть Fib1-процедуры включает rememberпараметр. Временные результаты вызова обоих процедур говорят сами за себя. По экспертным оценкам ассоциирование с процедурами remember-таблиц позволяет на целом ряде задач получать экспоненциальный временной выигрыш за счет определенных ресурсов памяти в виде требуемого места под remember-таблицу. В частности, для нашего примера remember-таблица Fib1-процедуры содержит 32 входа, что не соизмеримо с полученным временным выигрышем. Однако возможно и наоборот, например, при использовании процедур в циклических конструкциях (пример Proc_30-процедуры фрагмента). Для модификации remember-таблицы произвольной Proc-процедуры, определение которой содержало remember-параметр, наряду с приведенной выше Remember_T-процедурой можно использовать конструкции следующего простого вида:

Proc(<Фактические аргументы (ФА)>) := <Требуемый результат> {:|;}

T := op(4, eval(Proc)): T[(<ФА>)] := evaln(T[(<ФА>)]) {:|;}

По первой конструкции производится добавление в remember-таблицу Proc-процедуры входа, соответствующего указанным фактическим аргументам, которому будет соответствовать выход (требуемый результат), возвращаемый процедурой на заданных фактических аргументах. По второй конструкции производится удаление из remember-таблицы Proc-процедуры входа, соответствующего указанным фактическим аргументам. Следующий довольно прозрачный фрагмент иллюстрирует вышесказанное: