Смекни!
smekni.com

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

Использование последовательности свойств позволило определить целочисленную рекуррентную AG-функцию. Пример определения G3-функции иллюстрирует возможность использования специальных переменных args, nargs в define-определении функции, тогда как пример G4-функции дополнительно иллюстрирует определение функции от неопределенного числа аргументов. Однако, при этом следует иметь в виду, что использование указанных переменных в define-определении функций носит более ограниченный характер, чем при других способах определения функций пользователя. Более того, как показывают примеры определения двух эквивалентных функций G6 и G7, в плане представимости типов выражений, используемых при определении функции, функциональный (->)-оператор более предпочтителен. Примеры определения функций G8 и G9 также иллюстрируют ограничения define-способа задания функций. В этом отношении следует отметить, что реализация define-функции более младших релизов Maple во многих отношениях была более эффективной [8-10]. Наконец, последний пример фрагмента иллюстрирует (в ряде случаев весьма полезную) возможность рекурсивного использования функции define для определения функций пользователя.

Использование assign-процедуры для присвоения определения функции некоторому идентификатору (ее имени) позволяет включать определения функций непосредственно в вычислительные конструкции, соблюдая только одно правило: вычисление определения функции должно предшествовать ее первому вызову в вычисляемом выражении. Сказанное относится к любому способу определения функции, например:

> assign(G1, unapply([nargs, sum(args['k'], 'k'=1..nargs)]));

> assign(G2, () -> [nargs, sum(args['k'], 'k'=1..nargs)]);

> define(G3, G3()= nargs*sum(args[k], 'k'=1..nargs)), G1(42,47,67,62,89,96),

G2(42,47,67,62,89,96), G3(42,47,67,62,89,96); ⇒ [6, 403], [6, 403], 2418

> assign(Kr, unapply([nargs, sum(args[p], 'p'=1..nargs)])), Kr(10, 17); ⇒ [2, 27]

В частности, последний пример фрагмента иллюстрирует вычисление списочной структуры, содержащей unapply-определение Kr-функции, с последующим ее вызовом. Следует отметить при этом, что механизм пользовательских функций, поддерживаемый пакетом Mathemaica [6, 7], представляется нам существенно более гибким при реализации алгоритмов обработки.

При этом следует иметь ввиду, что в случае определения процедуры в assign-конструкции ее определение становится глобальным, если ее имя не определено в local-секции содержащей конструкцию процедуры, как это иллюстрирует следующий фрагмент:

> P:=proc() local Proc; assign(Proc = (() -> `+`(args))); Proc(args)/nargs end proc;

P := proc() local Proc; assign(Proc = (() -> `+`(args))); Proc(args)/nargs end proc > P(42,47,67), eval(Proc);

52, Proc

> restart; P:=proc() assign(Proc = (() -> `+`(args))); Proc(args)/nargs end proc;

P := proc() assign(Proc = (() -> `+`(args))); Proc(args)/nargs end proc > P(42,47,67), eval(Proc);

52, () -> `+`(args)

Еще об одном ухищрении стоит сказать особо. В релизах 6 и ниже по конструкции вида m(assign('a' = <Выражение>), где m – произволное целое либо NULL

можно было широко использовать assign-конструкции в выражениях, ибо возвращалось значение m с вычислением выражений в скобках, например:

> m:=10: (m(assign('a'=2006)) + a - 16)/(6(assign('b'=1995)) + b - 1); ⇒ 1 # Maple 6

Тогда как, начиная с релиза 7, данная возможность была исключена, например:

> m:=10: (m(assign('a'=2006)) + a - 16)/(6(assign('b'=1995)) + b - 1); # Maple 7 – 10 6 + a

5 + b

Что породило еще один тип несовместимости релизов пакета «снизу-вверх» для тех, кто пытался использовать особенности Maple-языка при программировании своих задач.

Рассмотрев способы определения процедур в Maple-языке и их структурную организацию, обсудим более детально отдельные компоненты структуры процедур, определяемых первым способом, как наиболее универсальным и часто используемым. Это тем более актуально, что позволит вам не только понимать реализацию пакетных процедур и процедур нашей Библиотеки [41] (некоторые из них представлены и в настоящей книге), но и самому приступать к созданию собственных конкретных приложений в среде Maple, реализованных в форме процедур и их библиотек.

3.2. Формальные и фактические аргументы Mapleпроцедуры

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

для процедуры числа формальных аргументов, как правило, идентифицируется ошибочная ситуация типа “Error, (in Proc) Proc uses a nth argument <Id>, which is missing”, указывающая на то, что Proc-процедуре было передано меньшее число фактических аргументов, чем имеется формальных аргументов в ее определении; где Id - идентификатор первого недостающего n-го фактического аргумента; (2) в случае числа фактических аргументов, большего определенного для процедуры числа формальных аргументов, ошибочной ситуации не идентифицируется и лишние аргументы игнорируются. Между тем, и в первом случае возможен корректный вызов. Это будет в том случае, когда в теле процедуры не используются формальные аргументы явно. Следующий простой фрагмент хорошо иллюстрирует вышесказанное:

> Proc:=proc(a, b, c) nargs, [args] end proc: Proc(5, 6, 7, 8, 9, 10), Proc(5), Proc(), Proc(5, 6, 7);

6, [5, 6, 7, 8, 9, 10], 1, [5], 0, [], 3, [5, 6, 7]

> Proc:= proc(a, b, c) a*b*c end proc: Proc(5, 6, 7), Proc(5, 6, 7, 8, 9, 10); ⇒ 210, 210 > Proc(645);

Error, (in Proc) Proc uses a 2nd argument, b, which is missing

> AVZ:= proc(x::integer, y, z::float) evalf(sqrt(x^3 + y^3)/(x^2 + y^2)*z) end proc:

> AVZ(20.06, 59, 64);

Error, AVZ expects its 1st argument, x, to be of type integer, but received 20.06

> AVZ(2006, 456);

Error, (in AVZ) AVZ uses a 3rd argument, z (of type float), which is missing

> [AVZ(64, 42, 19.42), AVZ(59, 42, 19.42, 78, 52)]; ⇒ [1.921636024, 1.957352295]

В момент вызова процедуры с передачей ей фактических выражений для ее соответствующих формальных аргументов первые предварительно вычисляются и их значения передаются в тело процедуры для замещения соответствующих им формальных аргументов, после чего производится вычисление составляющих тело Maple-предложений с возвратом значения последнего вычисленного предложения, если не было указано противного. В случае наличия в определении процедуры типированных формальных аргументов элементы последовательности передаваемых при ее вызове фактических значений проверяются на указанный тип и в случае несовпадения инициируется ошибочная ситуация, в противном случае выполнение процедуры продолжается. В качестве типов формальных аргументов процедуры используются любые из допустимых языком и тестируемых функцией type и процедурой whattype. При использования нетипированного формального аргумента рекомендуется все же указывать для него anything-тип, информируя других пользователей процедуры о том, что для данного формального аргумента допускаются значения любого типа, например, кодированием заголовка процедуры в виде proc(X::integer, Y::anything).

В качестве формальных аргументов могут выступать последовательности допустимых выражений Maple, типированных переменных, либо пустая последовательность. Типированная переменная кодируется в следующем формате: <Переменная>::<Тип>. Тип может быть как простым, так и сложным. При обнаружении в точке вызова процедуры фактического аргумента, типом отличающегося от определенного для соответствующего ему формального аргумента, возникает ошибочная ситуация с возвратом через lasterror-переменную соответствующей диагностики и с выводом ее в текущий сеанс, например:

> A:=proc(a::integer, b::float) a*b end proc: A(64, 42);

Error, invalid input: A expects its 2nd argument, b, to be of type float, but received 42

> lasterror;

"invalid input: %1 expects its %-2 argument, %3, to be of type %4, but received %5"

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

> A1:=proc(a::numeric, b::numeric) local a1, b1; assign(a1=a, b1=b); if not type(a, 'integer') then a1:=round(a) end if; if not type(b, 'float') then b1:=float(b) end if; a*b end proc: A1(64, 42), 17*A1(10/17, 59); ⇒ 2688, 590

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

При организации процедур роль типированных формальных аргументов не ограничивается только задачами проверки входной информации, но несет и ряд других важных нагрузок. В частности, использование uneval-типа для формального аргумента позволяет вне процедуры проводить его модификацию (т.е. обновлять на «месте» Maple-объект, определенный вне тела процедуры под этим идентификатором) как это иллюстрирует следующий фрагмент:

> A:= proc(L::list, a::anything) assign('L' = subs(a = NULL, L)) end proc:

> L:= [64, 59, 39, 44, 10, 17]; A(L, 64);

L := [64, 59, 39, 44, 10, 17]

Error, (in assign) invalid arguments

A1 := proc(L::uneval, a::anything) if not type( ,L 'symbol') then error "1st argument should be symbol but had received %1"whattype(, L)

elif type(eval(L), {'list', 'set'}) then assign ' '( L = subs( [`if`( not type( ,a {'list', 'set'}), a = NULL, seq(k = NULL, k = a))], eval(L) )) else error "1st argument should has type {list, set} but had received %1-type", whattype eval(( L))

end if

end proc

> A1(L, 64), L, A1(L, 59), L, A1(L, {59, 39, 44, 10, 17}), L;

[59, 39, 44, 10, 17], [39, 44, 10, 17], []

> A1(AVZ, 64), AVZ;