Наряду со сказанным, локальные переменные могут использоваться в теле процедур в качестве ведущих переменных с неопределенными для них значениями, например:
> y:= 64: GS:= proc(p) local y, F; F:= y -> sum(y^k, k= 0 .. n); diff(F(y), y$p) end proc: > [y, simplify(GS(2))]; ⇒ 64, y(n + 1) n2 − 2 yn n2 + y(n − 1)(ny − 2 − 1y)(3n + 1) n + y(n − 1) n + 2 yn − 2 |
Данный фрагмент иллюстрирует использование локальной y-переменной в качестве ведущей переменной F(y)-полинома от одной переменной. Вне области действия процедуры глобальная y-переменная имеет конкретное числовое значение, т.е. их области не пересекаются.
Следующий простой фрагмент иллюстрирует взаимосвязь локальных и глобальных переменных, когда вторые определяются как явно в global-секции, так и неявно через assignпроцедуру. Второй же пример фрагмента иллюстрирует механизм действия uneval-типированного формального аргумента, когда (в отличие от стандарта) ему могут присваиваться выражения на уровне глобальных переменных. Применение подобного весьма полезного приема уже иллюстрировалось в предыдущем разделе.
> P:= proc() local a; global b; x:= 56; assign('m' = 67); a:= proc(x) m:= 47; assign('n' = 89); x end proc; x*a(3) end proc:
Warning, `x` is implicitly declared local to procedure `P`
Warning, `m` is implicitly declared local to procedure `a`
> n, m:= 100, 100: P(), n, m; ⇒ 168, 89, 67
> P1:= proc(m::uneval) local a, x; global b; a, x:= 59, 39; b:= 64; assign('m'=42) end proc: > m:= 64: P1(m), a, x, b, m; ⇒ a, x, 64, 42
Еще на одном существенном моменте механизма глобальных и локальных переменных необходимо акцентировать внимание, предварительно пояснив понятие по-уровневого вычисления. Правила вычислений в Maple-языке предполагают нормальными полные вычисления для глобальных переменных и 1-уровневые для локальных. Поясним сказанное первым примером следующего простого фрагмента:
> W:= y^4; ⇒ W := y4 | (1) |
> y:= z^3; ⇒ y := z3 > z:= h^2; ⇒ z := h2 > h:= 3; ⇒ h := 3 > W; ⇒ 282429536481 > [eval(W, 1), eval(W, 2), eval(W, 3), eval(W, 4)]; ⇒ [y4, z12, h24, 282429536481] | (2) |
> G:= proc() local W, y, z, h; W:= y^4; y:= z^3; z:= h^2; h:= 2; W end proc: > [G(), eval(G()), evala(G()), evalf(G())]; ⇒ [y4, 16777216, y4, y4] | (3) |
в котором представлена простая рекуррентная цепочка выражений, вычисление которой реализует полностью рекуррентная подстановку и обеспечивает возврат конечного числового значения, т.е. производится полное вычисление для W-выражения, идентификатор которого полагается глобальным. С другой стороны, вызов функции eval(B, n) обеспечивает n-уровневое вычисление заданного ее первым фактическим В-аргументом выражения, что иллюстрирует второй пример фрагмента.
Для полного вычисления произвольного В-выражения используется вызов eval(B)-функции. Однако в первом примере фрагмента W-переменная является глобальной, что и определяет ее полное вычисление, если (как это иллюстрирует второй пример) не определено противного. Наконец, третий пример фрагмента иллюстрирует результат вычисления той же рекуррентной цепочки выражений, но уже составляющих тело процедуры и идентификаторы которых определены в ней локальными. Из примера следует, что если не определено противного, то процедура возвращает только первый уровень вычисления W-выражения и для его полного вычисления требуется использование функции eval, как это иллюстрирует последний пример фрагмента. Данное обстоятельство следует всегда иметь в виду, ибо оно не имеет аналогов в традиционных языках программирования и наряду с требованием особого внимания обеспечивает целый ряд весьма интересных возможностей программирования в различных приложениях.
Еще на одном моменте следует остановиться отдельно. По конструкциям op(2, eval(P)) и op(6, eval(P)) возвращается соответственно последовательность локальных и глобальных переменных процедуры P. И если в первом случае это действительно так, то во втором ситуация совершенно иная, а именно. Если в процедуре производятся присвоения переменным по assign-процедуре и эти переменные явно не определены глобальными, то второй вызов их не распознает, например:
> restart; P:= proc() local a, b, c; assign( x= 64, y = 59) end proc: P(): seq([op(k, eval(P))], k = [2, 6]), x, y; ⇒ [a, b, c], [], 64, 59
Это требует особого внимания при использовании в процедуре глобального уровня переменных во избежание нарушения вычислений в текущем сеансе при ее вызовах.
Globals := proc(P::procedure) local a b c f k p, , , , , ; assign(a = {op 6,( eval(P))}, p = {op 2,( eval(P))}, b = [ ], c = {anames '( user')}, f = cat(CDM( ), "\$Art18_Kr10$.m")); c := c minus {seq(`if`(cat "", ,( k " ")[1 .. 3] = "CM:", ,k NULL), k = c)}; for k in [Assign, assign, assign67, assign6, assign7 ] do try b := [op(b), op extrcalls( ,( P k))] catch "call of the form <%1> does not exist": next end try end do; (proc() save args, f end proc )(op(c)), Unassign(op(c)); for k in b do parse(cat "try eval(parse(", ,( k ")) catch : NULL end try;" '), statement') end do; c := a, ({anames ' ( user')} minus p) minus a; read f; c, fremove( )f end proc |
> P:= proc() local a; global x, y; a:= `+`(args); 5*assign(v=64), assign(g=59), assign(s=39); a/(v + g + s) end proc: Globals(P); ⇒ {y, x}, {v, s, g} > Globals(mwsname); ⇒ {}, {VGS_vanaduspension_14062005} > Globals(holdof); ⇒ {_avzagnartkrarn63}, {} > Globals(`type/file`); ⇒ {_datafilestate}, {_warning} > Proc:= proc() local a, b; global c; assign('x' = 64, 'c' = 2006), assign67('y' = 59, 39); a:=proc() local z, h; assign67('z' = 10, 17), assign(h = 59); [z], h end proc; a() end proc: [Proc()], Globals(Proc); ⇒ [[10, 17], 59], {c}, {x, y, z, h} > restart; Proc:= proc() local a, b; global c; assign('x' = 64, 'c' = 2006), assign67('y' = 59, 39); a:=proc() local z, h; assign67('z' = 10, 17), assign(h = 59); [z], h end proc; a() end proc: [Proc()]: c, x, [y], [z], h; ⇒ 2006, 64, [59, 39], [z], h > restart; P1:= proc() local a; assign('a' = 64); a end proc: P1(), a; ⇒ 64, a > restart; P1:= proc() local a; assign67('a' = 64); a end proc: P1(), a; ⇒ 64, a |
С этой целью нами была определена процедура Globals, обеспечивающая более точное тестирование глобальных переменных процедур. Вызов процедуры Globals(P) возвращает 2-элементную последовательность множеств глобальных переменных процедуры, определяемой фактическим аргументом Р. Первое множество возвращаемой последовательности содержит имена глобальных переменных, определяемых global-секцией процедуры Р, тогда как второе множество содержит имена глобальных переменных, которые определяются вызовами процедур пакетной assign и наших assign6, assign7, assign67 и Assign [41,103]. Процедура Globals функционирует в среде Maple 9 и выше. При этом, следует иметь в виду следующее важное обстоятельство, а именно.
Для простой процедуры (не содержащей внутри себя других процедур) вызов Globals(P) возвращает как определенные в global-секции Р переменные, так и переменные, значения которым присваивались процедурами assign, assign6, assign7, assign67 и Assign. Совсем иначе ситуация обстоит в случае вложенных процедур, когда вложенная процедура (подпроцедура) также содержит вызовы указанных процедур, но некоторые из вычисляемых ими переменных определены в local-секции подпроцедуры. В этом случае такие переменные, идентифицируясь Globals глобальными, на самом деле носят локальный характер. Сказанное хорошо иллюстрирует пример Proc-процедуры предыдущего фрагмента (переменные z и h). Данное обстоятельство следует учитывать при использовании процедуры Globals для вложенных процедур, содержащих assign-вызовы.
В целях повышения надежности при работе с глобальными переменными может быть использована процедура uglobal, чей вызов uglobal ('x', 'y', 'z',…) обеспечивает отмену значений глобальных переменных x, y, z, … на период выполнения процедуры, в которой данный вызов был сделан. После чего процедура может произвольно их использовать.
uglobal := proc() local a b, ; assign(a = cat(CDM( ), "/$Art_Kr169$"), b = interface(warnlevel)), null interface(( warnlevel = 0)); if type(a, 'file') then read3(a); fremove(a), null interface(( warnlevel = b)) else save3(args, a); unassign(args), null(interface(warnlevel = b)) end if end proc > V:=64: P:=proc() global V; uglobal('V'); V:= `*`(args); V,uglobal() end proc: P(10, 17, 39),V; 6630, 64 |
При этом, процедура uglobal в m-файле сохраняет значения всех переменных x, y, z, …, обеспечивая возможность их последующего восстановления вызовом uglobal(). Предыдущий фрагмент представляет исходный текст uglobal-процедуры и пример конкретного ее применения при работе с глобальной V-переменной в процедуре Р.
В Maple, начиная с 9-го релиза, для встроенной функции anames был определен аргумент 'user', по которому вызов anames('user') возвращает последовательность имен, значения для которых в текущем сеансе были определены пользователем. Данная возможность важна для приложений. В целях получения некоторого аналога данного средства для Maple релизов 8 и ниже нами была определена процедура pusers, исходный текст которой и пример применения представлены нижеследующим фрагментом.
pusers := proc() local a b c d k p h, , , , , , ; assign(a = { }, b = interface(warnlevel), c = "procedure ", d = " has been activated in the current session") interface(, warnlevel = 0); p := ({seq(`if`(cat "", ,( k " ")[1 .. 3] = "CM:", NULL k, ), k = {anames('procedure')})} minus {anames('builtin')}) minus {Testzero pusers, }; for k in p do unassign ' '( h ); try ParProc1(k h, ); if type(h, 'symbol') then a := {op(a), k} end if catch "invalid input: %1 expects": next end try end do; null interface(( warnlevel = b)), unassign '( _warning'), a end proc > Proc:= () -> `+`(args): Proc1:= () -> `+`(args): Proc2:= () -> `+`(args): Proc3:= () -> `+`(args): Proc4:= () -> `+`(args): pusers(); ⇒ {Proc, Proc1, Proc2, Proc3, Proc4} |
Вызов процедуры pusers() возвращает множество имен процедур, определенных пользователем в текущем сеансе. При этом, в него не включаются те процедуры, определения которых находятся в Maple-библиотеках пользователя, логически сцепленных с главной библиотекой пакета. Данная процедура функционирует в среде Maple релизов 6 – 10.