Смекни!
smekni.com

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

В целом ряде случаев возвращать результат вызова процедуры представляется весьма удобным через ее формальный h-аргумент, который кодируется в заголовке процедуры в последовательности ее формальных аргументов в виде h{::evaln}. Соответствующие же им фактические аргументы кодируются в виде {'H'|H}, т.е. процедуре сообщается, что ей передается не значение соответствующего ему фактического аргумента, а его идентификатор. В этом случае в теле процедуры производится присвоение данному фактическому аргументу-идентификатору требуемого значения, которое доступно сразу же после вызова процедуры. Следующий простой фрагмент иллюстрирует вышесказаное:

G := proc(L::list({integer, float, fraction}), x::evaln, y::evaln, z::evaln) local a b c k, , , ;

`&ma`(a b c, , , [ ]), seq `if` type( ,( ( k 'integer'), assign(' 'a = [op(a), k]),

`if`(type(k, 'fraction'), assign(' 'b = [op(b), k]), assign(' 'c = [op( )c , k]))), k = L),

assign (x = a, y = b, z = c), evalf sqrt(( `+` op(( L))))

end proc

> G([2006, 64, 10/17, 19.42, 59/1947, 350, 65.0, 16.10], a, v, z), a, v, z;

50.21094042 [, 2006 64 350, , ], 1017, 331 , [19.42 65.0 16.10, , ]  G1 := proc(L::list, x::evaln, y::evaln, z::evaln) local a b c d k, , , , ; `&ma`(a b c d, , , , [ ]), seq `if` type( ,( ( k 'integer'), assign(' 'a = [op(a), k]), `if`( type(k, 'fraction'), assign(' 'b = [op(b), k]), `if` type( ,( k 'float'), assign(' 'c = [op( )c , k]), assign(' 'd = {op(d), whattype eval( )( k )})))), k = L), assign(x = a, y = b, z = c, `if`(4 < nargs, assign(args[5] = d), NULL)), evalf sqrt(( `+` op(( L)))) end proc > G1([2006,64,10/17,19.42,59/1947,350,65.0,16.10,10+17*I,64-42*I], a, v, z, 'h'), a, v, z, h;

10 1  50.94309321 − 0.2453718299 I, [2006 64 350, , ], 

, , [19.42 65.0 16.10, , ], {complex} 17 33

Первый пример фрагмента представляет G-процедуру, допускающую при своем вызове четыре фактических аргумента, из которых три последних имеют evaln-тип и через которые передаются списки фактических аргументов, имеющих типы float, integer и fraction соответственно. Тогда как в качестве основного возврата процедуры является результат вычисления корня квадратного из суммы фактических аргументов, переданных процедуре при ее вызове. Пример вызова процедуры G иллюстрирует сказанное. Второй пример фрагмента представляет G1-процедуру, являющуюся модификацией предыдущей процедуры и допускающей при своем вызове более четырех фактических аргумента, из которых четыре первых аналогичны случаю процедуры G, тогда как через пятый необязательный аргумент передается множество типов фактических аргументов процедуры, отличных от типов {float, integer, fraction}. Тогда как основной возврат процедуры аналогичен случаю G-процедуры. Пример вызова процедуры иллюстрирует сказанное. В обеих процедурах рекомендуется обратить внимание на использование процедуры/орператора &ma [103], обеспечивающего присвоение одного и того же выражения последовательности переменных, и assign-процедуры для присвоения значений фактическим аргументам, через которые обеспечиваются вторичные возвраты процедуры.

Между тем, возвращать вторичные результаты вычислений процедур возможно и через ее фактические аргументы, не определяемые evaln-типом и имеющие symbol-тип; в этом случае их рекомендуется кодировать в невычисленном формате, т.е. в прямых верхних кавычках. Следующий фрагмент на процедуре Kris иллюстрирует описанный подход.

Kris := proc(x, y, z) local a k, ; for k in [args] do if type(k, 'assignable') then a := k; assign(a = map(whattype, map(eval, [args]))); WARNING("2nd result had been returned via <%1>", a); return [args]

end if

end do;

[args] end proc

> Kris(59, 64.42, z, [4, 1, 2007], x);

Warning, 2nd result had been returned via <z>

[59, 64.42, z, [4, 1, 2007], x]

> z; ⇒ [integer, float, symbol, list, symbol]

> Kris(59, 64.42, 65, [4, 1, 2007]); ⇒ [59, 64.42, 65, [4, 1, 2007]]

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

Maple вычисляет формальные аргументы один раз, поэтому их не следует использовать в теле процедуры подобно локальным переменным. Формальному аргументу, через который будет возвращаться результат вызова процедуры, в ее теле должно быть выполнено только одно присвоение, определяющее возвращаемый результат, отличный от основного. При этом, в ряде случаев процедура может вполне обходиться без основного возврата, ограничиваясь только вторичными, а то даже и вовсе без них. В обоих случаях вызов такой процедуры возвращает NULL-значение, т.е. ничего. Механизм возврата результатов вызова Maple-процедуры через ее фактические аргументы дает возможность наряду со стандартным или на основе RETURN-функции подходами организовывать дополнительные (вторичные) выходы, определяемые наличием или отсутствием при вызове процедуры соответствующих аргументов. Подобная организация возврата результатов вызова достаточно широко практикуется в библиотечных процедурах как собственно самого пакета Maple, так и наших [41,103,109].

Механизм возврата результатов вызова Maple-процедуры через ее фактические аргументы позволяет наряду со стандартным либо на основе RETURN-функции (return-предложения) подходами организовывать и дополнительные выходы, определяемые как наличием либо отсутствием при вызове процедуры соответствующих аргументов, так и выполнением оп-еделенных условий. Такая организация возврата результата вызова широко практикуется в пакетных процедурах. Наряду с представленными выше механизмами возврата результатов вызова процедуры можно предложить еще один механизм, полезный в целом ряде приложений и используемый рядом процедур нашей Библиотеки [103,109]. Суть его состоит в следующем.

Результаты вызова процедуры возвращаются через ее ключевые аргументы, представляющие собой некоторые идентификаторы. Эти переменные относительно процедуры выступают на уровне глобальных переменных и их определение производится в теле процедуры, реализуясь лишь при указании такого аргумента при вызове процедуры. Схематично такую процедуру Proc можно представить следующим фрагментом.

Proc:= proc({Id1} {, Id2} … {, Idp}) local t; if member('Id1', [args], 't') then assign(args[t] = expr1) end if; if member('Id2', [args], 't') then assign(args[t] = expr2) end if;

……………………………………………………………...….………

`if`(member('Idp', [args], 't'), assign(args[t] = exprp), NULL) end proc:

где exprk – Maple-выражение (k=1..p). При этом, следует иметь в виду, что здесь требуется использование assign-процедуры, а не (:=)-оператора присваивания, т.е. assign(args[t] = expr), а не args[t] := expr. В противном случае Maple выводит предупреждение о недопустимости использования переменной args в качестве локальной переменной с последующим инициированием ошибочной ситуации с диагностикой «Error, args cannot be declared as a local». Это еще один пример принципиальных различий оператора присваивания (:=) и assign-процедуры.

Таким образом, необязательное ключевое слово Idk является глобальной переменной, получающей значение при вызове процедуры Proc(…, Idk, …), т.е. процедура через него возвращает выражение, присвоенное в теле процедуры. При этом, наряду с описанным в качестве механизмов возврата результатов вызова процедура могут использовать и другие, описанные выше. Более того, наряду с ключевыми аргументами в качестве формальных аргументов процедуры могут использоваться и другие типы.

Данная организация возврата результатов вызова процедуры в целом ряде случаев оказывается довольно эффективной, позволяя: (1) легко варьировать возврат требуемого кортежа глобальных переменных и (2) достаточно легко расширять набор функций, поддерживаемых процедурой, при весьма простой модификации ее исходного текста.

Как уже отмечалось выше, определение процедуры допускает следующий формат:

Proc := proc(args)::type; … end proc

который допустим только в релизах 7 и выше пакета, вызывая в Maple 6 ошибку.

Кодирование за заголовком процедуры типа не является в полном смысле слова типированием возвращаемого процедурой результата, а скорее утверждением (assertion). При установке kernelopts(assertlevel=2) производится проверка типа возвращаемого результата вызова процедуры. Если тип не соответствует утверждению, то возникает ошибочная ситуация с диагностикой "assertion failed: %1 expects its return value to be of type %2, but computed %3". При остальных установках assertlevel–опции утверждение игнорируется. Особого смысла в данном формате мы не видим и вот почему. Если необходимо типировать результат возврата, то намного удобнее и эффективнее это делать в самой процедуре на основе как реализуемого ею алгоритма, так и получаемых типов фактических аргументов. При этом, сохраняется непрерывность вычислений и производится обработка ошибочных и особых ситуаций, связанных с типом возвращаемого результата. Более того, при качественной разработке процедуры в ней уже должна быть предусмотрена (при необходимости) проверка типов получаемых ею фактических аргументов. Так что и здесь имеется «фильтр» на допустимось фактических аргументов. К тому же, если такого формата процедура находится в библиотеке и используется для программирования, то при возникновении ошибки указанного выше типа пользователю будет весьма сложно обнаружить причину такой ошибки, привязанной лишь к результату вызова процедуры, которая может иметь и несколько разнотипных точек возврата В одном лишь, пожалуй, можно согласиться, что такого рода «типизация» в некотором роде подобна типизации формальных аргументов, но относится к результатам ее вызова, и может в ряде случаев представить интерес, например, в случае множественности точек возврата процедуры, упрощая обработку типов результатов вызова. Следующий простой фрагмент иллюстрирует результат использования указанного формата кодирования процедуры: