Ознакомительная версия.
Следующий пример поясняет применение оператора global в процедуре (файл р5):
> а:=1;b:=1;
а := 1 b := 1
> fg:=proc(х, у)
> global a,b;
> а:=х^2:b:=у^2:
> RETURN(sqrt(a+b));
> end;
fg :=
proc(x, y)
global a, b; a:= х^2; b:= у^2; RETURN (sqrt (a+b))
end proc> fg(3, 4);
5
> [a,b];
[9, 16]
В примере переменным а и b вначале присвоены значения 1. Поскольку они в процедуре объявлены глобальными, то внутри процедуры они принимают новые значения х2 и у2. В результате при выходе из процедуры они имеют уже новые значения. Это и есть побочный эффект при исполнении данной процедуры. Если пользователь не знает (или не помнит), что та или иная процедура имеет побочный эффект, то он рискует получить самые неожиданные (и неверные) результаты своих вычислений.
Следует отметить, что нельзя делать глобальными переменные, указанные в списке параметров процедуры, поскольку они уже фактически объявлены локальными. Такая попытка приведет к появлению сообщения об ошибке следующего вида «Error, argument and global `х` have the same name». При этом соответствующие переменные останутся локальными
10.3.8. Функция вывода сообщений об ошибках ERROR
При профессиональной подготовке процедур пользователь должен предусмотреть их поведение при возможных ошибках. Например, если он готовит процедуру или функцию, вычисляющую квадратный корень из действительных чисел, то надо учесть, что такой корень нельзя извлекать из отрицательных чисел (будем, исключительно в учебных целях, считать, что комплексные числа в данном примере недопустимы).
Для контроля за типом данных обычно используются различные функции оценки и тестирования. При выявлении ими ошибки, как правило, предусматривается вывод соответствующего сообщения. Для этого используется функция ERROR.
ERROR(expr_1, expr_2, ...)
где exp_1, … — ряд выражений (возможно, пустой). Наиболее часто ERROR выводит просто строковое сообщение об ошибке, например ERROR(`string`). Полное сообщение об ошибке имеет вид.
Error, (in name) string, ...
Приведем пример процедуры, в которой предусмотрен вывод сообщения об ошибке при задании переменной х < 0 (файл р5):
> f := proc(х) if х<0 then error "invalid variable x: %1", x else
х^(1/2) end if end proc;
f:= proc(x)
if x < 0 then error "invalid variable x:%1", x else х^(1/2) end if
end proc
> f(3.) ;
1.732050808
> f(-3.);
Error, (in f) invalid variable x: -3.
> lasterror;
"invalid variable x: %1", -3
> lastexception;
f, "invalid variable x: %1",-3
Эта процедура вычисляет квадратный корень из числа х. При х<0 выводится заданное сообщение об ошибке. Еще раз обращаем внимание читателя на учебный характер данного примера, поскольку вычисление квадратного корня (в том числе из комплексных и отрицательных действительных чисел) реализовано встроенной функцией sqrt.
10.3.9. Ключи в процедурах
В объявление процедуры можно включить ключевые слова, вводимые словом
options opseq
Иногда их называют расширяющими ключами. Предусмотрены следующие ключи:
arrow — определят процедуру-оператор в нотации ->;
builtin — определяет функцию как встроенную;
call external — задает обращение к внешним программным модулям;
copyright — защищает процедуру от копирования.
inline — определяет процедуру как подчиненную (возможно не для всех процедур — см. справку).
load=memberName — загружает нужный для определений процедуры модуль (см. также опцию unload и детали в справке);
operator — объявляет процедуру — функциональный оператор;
system — определяет процедуру как системную,
remember — определяет таблицу памяти для процедуры;
trace — задает трассировку процедуры;
unload=memberName — выгружает нужный для определений процедуры модуль (см. опцию load).
Ключ remember обеспечивает занесение результатов обращений к процедуре в таблицу памяти, которая используется при исполнении процедуры. Функция ор позволяет вывести таблицу (файл p6):
> f:=proc(x) options remember; x^3 end:
> f(2);
8
> f(3);
27
> op(4,eval(f));
table([2 = 8, 3 = 27])
Ключ remember особенно полезен при реализации итерационных процедур. К примеру, в приведенной ниже процедуре (без использования ключа remember) время вычисления n-го числа Фибоначчи растет пропорционально квадрату n:
> f:=proc(n) if n<2 then n else f(n-1)+f(n-2) fi end;
f :=
proc(n)
if n < 2
then n
else f(n - 1)+f(n-2)
end if end proc> time(f(30));
4.891
> f(30);
832040
Вычисление f(30) по этой процедуре на ПК с процессором Pentium 4 НТ 2,6 ГГц в системе Maple 9.5 время вычисления составляет менее 5 с — см. контроль этого времени с помощью функции time (результат в секундах).
Стоит добавить в процедуру ключ remember, и время вычислений резко уменьшится:
> restart;
> fe:=proc(n) options remember; if n<2 then n else fe(n-1)+fe(n-2) fi
end:
> fe(30);
832040
> time(fe(30));
0.
При этом вычисление fe(30) происходит практически мгновенно, так как все промежуточные результаты в первом случае вычисляются заново, а во втором они берутся из таблицы. Однако это справедливо лишь тогда, когда к процедуре было хотя бы однократное обращение. Обратите внимание на то, что данные процедуры являются рекурсивными — в их теле имеется обращение к самой себе.
Ключ builtin придает процедуре статус встроенной. Он должен использоваться всегда первым. С помощью функции eval(name) можно проверить, является ли функция с именем name встроенной:
> eval(type);
proc() option builtin, 274
end proc> eval(print) ;
proc() option builtin, 235
end procЧисла в теле процедур указывают системные номера функций. Следует отметить, что в новой версии Maple 9 они существенно отличаются от принятых в предшествующих версиях (даже Maple 8).
Этот ключ придает процедуре статус системной. У таких процедур таблица памяти может быть удалена. У обычных процедур таблица памяти не удаляется и входит в так называемый «мусорный ящик» (garbage collector).
10.3.13. Ключи operator и arrow
Эта пара ключей задает процедуре статус оператора в «стрелочной» нотации (->). Это достаточно пояснить следующими примерами:
> o:=proc(x,y) option operator , arrow; x-sqrt(у) end;
о:=(x,y)→x-√y
> о(4, 2);
4 -
√2
> о(4, 2.);
2.585786438
Ключ trace задает вывод отладочной информации:
> o:=proc(x,y) option trace, arrow; x-sqrt(y) end;
о: =
proc(x, y)
option trace, arrow, x - sqrt(y)
end proc> о(4, 2.);
{—> enter o, args =4, 2.
2.585786438
<- exit о (now at top level) = 2.585786438}
2.585786438
Этот ключ защищает тело процедуры от просмотра. Это поясняют следующие два примера:
> o:=proc(x,y) x-sqrt(у) end;
о: =
proc(x, х - sqrt(у)
end proc> oo:=proc(х,у) option Copyright; x-sqrt(у) end;
oo:=
proc(x,у) …
end proc> oo(4,2.);
2.585786438
Нетрудно заметить, что во втором примере тело процедуры уже не просматривается. Для отмены защиты от просмотра можно использовать оператор interface(verboseproc=2).
10.3.16. Общая форма задания процедуры
Выше мы рассмотрели основные частные формы задания процедур. Все они могут быть объединены в общую форму задания процедуры:
name:=proc(<argseq>) # объявление процедуры
local<nseq>; # объявление локальных переменных
global<nseq>; # объявление глобальных переменных
uses<useg> # объявление структур use ... in ... end use
options<nseq>; # объявление расширяющих ключей
description<stringseq>; # объявление комментарий
<stateq> # выражения — тело процедуры
end; (или end:) # объявление конца процедуры
Эта форма охватывает все описанные выше частные формы и позволяет готовить самые сложные и надежно работающие процедуры. Читателям-программистам стоит детально изучить по справке возможности этой формы задания процедур, а также возможности конструкции use … in … end use.
Ознакомительная версия.