Компания ALT Linux
Опубликован: 24.03.2015 | Доступ: свободный | Студентов: 550 / 136 | Длительность: 19:00:00
Лекция 4:

Численные методы и программирование с Maxima

4.1.3 Блоки

Как в условных выражениях, так и в циклах вместо простых операторов можно писать составные операторы, т.е. блоки. Стандартный блок имеет вид: block([r,s,t],r:1,s:r+1,t:s+1,x:t,t*t); Сначала идет список локальных переменных блока (глобальные переменные с теми же именами никак не связаны с этими локальными переменными). Список локальных переменных может быть пустым. Далее идет набор операторов. Упрощенный блок имеет вид: (x:1,x:x+2,a:x); Обычно в циклах и в условных выражениях применяют именно эту форму блока. Значением блока является значение последнего из его операторов. Внутри данного блока допускаются оператор перехода на метку и оператор return. Оператор return прекращает выполнение текущего блока и возвращает в качестве значения блока свой аргумент block([],x:2,x:x*x, return(x), x:x*x);

В отсутствие оператора перехода на метку, операторы в блоке выполняются последовательно. (В данном случае слово "метка" означает отнюдь не метку типа "%i5" или "%o7"). Оператор go выполняет переход на метку, расположенную в этом же блоке:

(%i1)	block([a],a:1,metka, a:a+1,
	if a=1001 then return(-a),go(metka));
(\%o1) -1001

В этом блоке реализован цикл, который завершается по достижении "переменной цикла" значения 1001. Меткой может быть произвольный идентификатор.

Следует иметь в виду, что цикл сам по себе является блоком, так что (в отличие от языка C) прервать выполнение циклов (особенно вложенных циклов) с помощью оператора go невозможно, т.к. оператор go и метка окажутся в разных блоках. То же самое относится к оператору return. Если цикл, расположенный внутри блока, содержит оператор return, то при исполнении оператора return произойдет выход из цикла, но не выход из блока:

(%i1)	block([],x:for i:1 thru 15 do
	if i=2 then return(555),display(x),777);
x=555
777\leqno{(\%o1) }
(%i2)	block([],x:for i:1 thru 15 do
	if i=52 then return(555),display(x),777);
x=done
777\leqno{(\%o2) }

Если необходимо выйти из нескольких вложенных блоков сразу (или нескольких блоков и циклов сразу) и при этом возвратить некоторое значение, то следует применять блок catch

(%i3)	catch( block([],a:1,a:a+1, throw(a),a:a+7),a:a+9 );
2\leqno{(\%o3) }
(%i4)	a;
2\leqno{(\%o4) }
(%i5)	catch(block([],for i:1 thru 15 do
	if i=2 then throw(555)),777);
555\leqno{(\%o5) }

В данном блоке выполнение цикла завершается, как только значение i достигает 2. Возвращаемое блоком catch значение равно 555.

(%i6)	catch(block([],for i:1 thru 15 do
	if i=52 then throw(555)),777);
777\leqno{(\%o6) }

В данном блоке выполнение цикл выполняется полностью, и возвращаемое блоком catch значение равно 777 (условия выхода из цикла при помощи throw не достигаются).

Оператор throw — аналог оператора return, но он обрывает не текущий блок, а все вложенные блоки вплоть до первого встретившегося блока catch.

Наконец, блок errcatch позволяет перехватывать некоторые (к сожалению, не все!) из ошибок, которые в нормальной ситуации привели бы к завершению счета.

Пример:

(%i1)	errcatch(a:1, b:0, log(a/b), c:7);
expt:	undefined: 0 to a negative exponent.
[\ ]\leqno{(\%o1) }
(%i2)	c;
c\leqno{(\%o2) }

Выполнение последовательности операций прерывается на первой операции, приводящей к ошибке. Остальные выражения блока не выполняются (значение c остаётся неопределённым). Сообщение об возникшей ошибке может быть выведено функцией errormsg().

4.1.4 Функции

Наряду с простейшим способом задания функции, Maxima допускает создание функции в виде последовательности операторов: f(x) :=(expr_1, expr_2, ..., expr_n); Значение, возвращаемое функцией — значение последнего выражения expr_n.

Чтобы использовать оператор return и изменять возвращаемое значение в зависимости от логики работы функции, следует применять конструкцию block, например: f(x)=block ([], expr_1, ..., if (a > 10) then return(a), ..., expr_n).

При a > 10 выполняется оператор return и функция возвращает значение a, в противном случае — значение выражения expr_n.

Формальные параметры функции или блока — локальные, и являются видимыми только внутри них. Кроме того, при задании функции можно объявить локальные переменные (в квадратных скобках в начале объявления функции или бока).

Пример:

block ([a: a], expr_1, ..., a: a+3, ..., expr_n)

В данном случае при объявлении блока в локальной переменной a сохраняется значение глобальной переменной a, определённой извне блока.

Пример:

(%i1)	f(x):=([a:a],if a>0 then 1 else (if a<0 then -1 else 0));
\[\mathrm{f}\left( x\right) :=\left( [a:a],\mathrm{if}\  a>0\  \mathrm{then}\  1\  \mathrm{else}\  \mathrm{if}\  a<0\  \mathrm{then}\  -1\  \mathrm{else}\  0\right) \leqno{(\%o1) }\]
(%i2)	a:1;
1\leqno{(\%o2) }
(%i3)	f(0);
1\leqno{(\%o3) }
(%i4)	a:-4;
-4\leqno{(\%o4) }
(%i5)	f(0);
-1\leqno{(\%o5) }
(%i6)	a:0;
0\leqno{(\%o6) }
(%i7)	f(0);
0\leqno{(\%o7) }

В данном примере значение переменной a задаётся вне тела функции, но результат, возвращаемый ею, зависит от значения a.

Начальные значения локальных переменных функции могут задаваться двумя способами:

  • Задание функции f(x) := (expr_1, ..., expr_n);, вызов функции f(1); — начальное значение локальной переменной x равно 1.
  • Задание блока block ([x: 1], expr_1, ..., expr_n), при этом начальное значение локальной переменной x также равно 1.

Наряду с именованными функциями, Maxima позволяет использовать и безымянные функции (лямбда-функции). Синтаксис использования лямбда-выражений (правда, при использовании с лямбдавыражениями всё-таки ассоциируется имя — см. пример):

f1: lambda ([x_1, \dots, x_m], expr_1, \dots, expr_n)\\
f2: lambda ([[L]], expr_1, \dots, expr_n)\\
f3: lambda ([x_1, \dots, x_m, [L]], expr_1, \dots, expr_n)\\

Пример:

(%i1)	f: lambda ([x], x^2);
lambda\left( [x],{x}^{2}\right) \leqno{(\%o1) }
(%i2)	f(a);
{a}^{2}\leqno{(\%o2) }

Более сложный пример (лямбда-выражения могут использоваться в контексте, когда ожидается имя функции):

(%i3)	lambda ([x], x^2) (a);
{a}^{2}\leqno{(\%o3) }
(%i4)	apply (lambda ([x], x^2), [a]);
{a}^{2}\leqno{(\%o4) }
(%i5)	map (lambda ([x], x^2), [a, b, c, d, e]);
[{a}^{2},{b}^{2},{c}^{2},{d}^{2},{e}^{2}]\leqno{(\%o5) }

Аргументы лямбда-выражений — локальные переменные. Другие переменные при вычислении лямбда-выражений рассматриваются как глобальные. Исключения отмечаются специальным символом — прямыми кавычками (см. лямбда-функцию g2 в примере).

(%i6)	a: %pi$ b: %e$ g: lambda ([a], a*b);
lambda\left( [a],a\,b\right) \leqno{(\%o8) }
(%i9)	b: %gamma$ g(1/2);
\frac{\gamma}{2}\leqno{(\%o10) }
(%i11)	g2: lambda ([a], a*"b);
lambda\left( [a],a\,\gamma\right) \leqno{(\%o11) }
(%i12)	b: %e$ g2(1/2);
\frac{\gamma}{2}\leqno{(\%o13) }

Лямбда-функции могут быть вложенными. При этом локальные переменные внешнего выражения доступны как глобальные для внутреннего (одинаковые имена переменных маскируются).

Пример:

(%i1)	h: lambda ([a, b], h2: lambda ([a], a*b), h2(1/2));
lambda\left( [a,b],h2:lambda\left( [a],a\,b\right) ,h2\left(
\frac{1}{2}\right) \right) \leqno{(\%o1) }
(%i2)	h(%pi, %gamma);
\frac{\gamma}{2}\leqno{(\%o2) }

Подобно обычным функциям, лямбда-функции могут иметь список параметров переменной длины.

Пример:

(%i1)	f : lambda ([aa, bb, [cc]], aa * cc + bb);
lambda\left( [aa,bb,[cc]],aa\,cc+bb\right) \leqno{(\%o1) }
(%i2)	f(3,2,a,b,c);
[3\,a+2,3\,b+2,3\,c+2]\leqno{(\%o2) }

Список [cc] при вызове лямбда-функции f включает три элемента: [a,b,c]. Формула для расчёта f применяется к каждому элементу списка.

Локальные переменные могут быть объявлены и посредством функции local (переменные v_1,v_2,... ,v_n объявляются локальными вызовом local(v_1,v_2,... ,v_n) независимо от контекста).