Численные методы и программирование с 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.
(%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));
(%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 позволяет использовать и безымянные функции (лямбда-функции). Синтаксис использования лямбда-выражений (правда, при использовании с лямбдавыражениями всё-таки ассоциируется имя — см. пример):
Пример:
(%i1) f: lambda ([x], x^2);
(%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]);
Аргументы лямбда-выражений — локальные переменные. Другие переменные при вычислении лямбда-выражений рассматриваются как глобальные. Исключения отмечаются специальным символом — прямыми кавычками (см. лямбда-функцию в примере).
(%i6) a: %pi$ b: %e$ g: lambda ([a], a*b);
(%i9) b: %gamma$ g(1/2);
(%i11) g2: lambda ([a], a*"b);
(%i12) b: %e$ g2(1/2);
Лямбда-функции могут быть вложенными. При этом локальные переменные внешнего выражения доступны как глобальные для внутреннего (одинаковые имена переменных маскируются).
Пример:
(%i1) h: lambda ([a, b], h2: lambda ([a], a*b), h2(1/2));
(%i2) h(%pi, %gamma);
Подобно обычным функциям, лямбда-функции могут иметь список параметров переменной длины.
Пример:
(%i1) f : lambda ([aa, bb, [cc]], aa * cc + bb);
(%i2) f(3,2,a,b,c);
Список [] при вызове лямбда-функции включает три элемента: []. Формула для расчёта применяется к каждому элементу списка.
Локальные переменные могут быть объявлены и посредством функции (переменные объявляются локальными вызовом независимо от контекста).