Россия |
Численные методы и программирование с 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));

В этом блоке реализован цикл, который завершается по достижении "переменной цикла" значения 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);


(%i2) block([],x:for i:1 thru 15 do if i=52 then return(555),display(x),777);


Если необходимо выйти из нескольких вложенных блоков сразу (или нескольких блоков и циклов сразу) и при этом возвратить некоторое значение, то следует применять блок catch
(%i3) catch( block([],a:1,a:a+1, throw(a),a:a+7),a:a+9 );

(%i4) a;

(%i5) catch(block([],for i:1 thru 15 do if i=2 then throw(555)),777);

В данном блоке выполнение цикла завершается, как только значение достигает 2. Возвращаемое блоком catch значение равно 555.
(%i6) catch(block([],for i:1 thru 15 do if i=52 then throw(555)),777);

В данном блоке выполнение цикл выполняется полностью, и возвращаемое блоком 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) }](/sites/default/files/tex_cache/b6d7a43614ec93f79208d1f468e06c75.png)
(%i2) c;

Выполнение последовательности операций прерывается на первой операции, приводящей к ошибке. Остальные выражения блока не выполняются (значение c остаётся неопределённым). Сообщение об возникшей ошибке может быть выведено функцией .
4.1.4 Функции
Наряду с простейшим способом задания функции, Maxima допускает создание функции в виде последовательности операторов: f(x) :=); Значение, возвращаемое функцией — значение последнего выражения
.
Чтобы использовать оператор return и изменять возвращаемое значение в зависимости от логики работы функции, следует применять конструкцию block, например: f(x)=block ([], , ..., if (a > 10) then return(a), ...,
).
При выполняется оператор return и функция возвращает значение
, в противном случае — значение выражения
.
Формальные параметры функции или блока — локальные, и являются видимыми только внутри них. Кроме того, при задании функции можно объявить локальные переменные (в квадратных скобках в начале объявления функции или бока).
Пример:
block ([a: a], , ..., a: a+3, ...,
)
В данном случае при объявлении блока в локальной переменной сохраняется значение глобальной переменной
, определённой извне блока.
Пример:
(%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) }\]](/sites/default/files/tex_cache/3a2e3c5c89e30418fcd0f9a9c6a0fbcb.png)
(%i2) a:1;

(%i3) f(0);

(%i4) a:-4;

(%i5) f(0);

(%i6) a:0;

(%i7) f(0);

В данном примере значение переменной задаётся вне тела функции, но результат, возвращаемый ею, зависит от значения
.
Начальные значения локальных переменных функции могут задаваться двумя способами:
- Задание функции f(x) := (
, ...,
);, вызов функции f(1); — начальное значение локальной переменной x равно 1.
- Задание блока block ([x: 1],
, ...,
), при этом начальное значение локальной переменной
также равно 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)\\](/sites/default/files/tex_cache/c39b7553c0d05db2ed74402ddb6d7a8f.png)
Пример:
(%i1) f: lambda ([x], x^2);
![lambda\left( [x],{x}^{2}\right) \leqno{(\%o1) }](/sites/default/files/tex_cache/1e2cd95fe4dd806c37557f2c36ffcfdb.png)
(%i2) f(a);

Более сложный пример (лямбда-выражения могут использоваться в контексте, когда ожидается имя функции):
(%i3) lambda ([x], x^2) (a);

(%i4) apply (lambda ([x], x^2), [a]);

(%i5) map (lambda ([x], x^2), [a, b, c, d, e]);
![[{a}^{2},{b}^{2},{c}^{2},{d}^{2},{e}^{2}]\leqno{(\%o5) }](/sites/default/files/tex_cache/5ff2693060410c404ad231f31a34da4a.png)
Аргументы лямбда-выражений — локальные переменные. Другие переменные при вычислении лямбда-выражений рассматриваются как глобальные. Исключения отмечаются специальным символом — прямыми кавычками (см. лямбда-функцию в примере).
(%i6) a: %pi$ b: %e$ g: lambda ([a], a*b);
![lambda\left( [a],a\,b\right) \leqno{(\%o8) }](/sites/default/files/tex_cache/3e3ec90a3f29cb22f5604a22b8adb6a5.png)
(%i9) b: %gamma$ g(1/2);

(%i11) g2: lambda ([a], a*"b);
![lambda\left( [a],a\,\gamma\right) \leqno{(\%o11) }](/sites/default/files/tex_cache/cc52d76a976c816dbcc0d164963af5e7.png)
(%i12) b: %e$ g2(1/2);

Лямбда-функции могут быть вложенными. При этом локальные переменные внешнего выражения доступны как глобальные для внутреннего (одинаковые имена переменных маскируются).
Пример:
(%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) }](/sites/default/files/tex_cache/5389efa5e2098dd0065a759ab5f4f31c.png)
(%i2) h(%pi, %gamma);

Подобно обычным функциям, лямбда-функции могут иметь список параметров переменной длины.
Пример:
(%i1) f : lambda ([aa, bb, [cc]], aa * cc + bb);
![lambda\left( [aa,bb,[cc]],aa\,cc+bb\right) \leqno{(\%o1) }](/sites/default/files/tex_cache/53f463b1c8cc6c3e8a9d56a5e0ce0d58.png)
(%i2) f(3,2,a,b,c);
![[3\,a+2,3\,b+2,3\,c+2]\leqno{(\%o2) }](/sites/default/files/tex_cache/6b23c86470f7cab667b0b9d5ca300162.png)
Список [] при вызове лямбда-функции
включает три элемента: [
]. Формула для расчёта
применяется к каждому элементу списка.
Локальные переменные могут быть объявлены и посредством функции (переменные
объявляются локальными вызовом
независимо от контекста).