Компьютерная алгебра в программе Mathematica 4

         

Использование процедур


  В основе процедурного программирования лежит понятие процедуры и типовых средств управления — циклов, условных и безусловных выражений и т. д. Процедурный подход


— самый распространенный в программировании, и разработчики Mathematica были вынуждены обеспечить его полную поддержку. Однако программирование систем Mathematica и в этом случае остается функциональным, поскольку элементы процедурного программирования существуют в конечном счете в виде функций.

Процедуры являются полностью самостоятельными программными модулями, которые задаются своими именами и отождествляются с выполнением некоторой последовательности операций. Они могут быть заданы в одной строке с использованием в качестве разделителя символа «;» (точка с запятой). Вот пример задания однострочной процедуры, отождествленной с именем г:

r = (1 + х)^2; r= Expand[г]; r - 1

2Х+Х2

Обратите внимание на то, что в теле процедуры символ г используется как вспомогательная переменная. Эта процедура возвращает символьное выражение

Expand[ (1+х)^2] - 1.

В общем случае в теле процедуры могут находиться произвольные выражения, разумеется, с синтаксисом, присущим языку программирования системы. Процедура может не возвращать никаких значений, а просто выполнять определенный комплекс операций. Область записи подобных элементарных процедур ограничена ячейкой (строкой) ввода.

Для задания процедуры со списком локальных переменных {а, b,...} и телом ргос может использоваться функция Module [ {а, b,...} ,ргос]. С применением этой функции мы столкнемся позже.

Для создания полноценных процедур и функций, которые могут располагаться в любом числе строк, может использоваться базовая структура — блок:

  • Block [{x, у,...}, procedure] — задание процедуры с декларацией списка локальных переменных х, у,...;
  • Block[{x = х0, у=у0,...}, procedure] — задание процедуры с декларацией списка переменных х, у,... с заданными начальными значениями.
Пример использования базовой структуры:

g[x_] := Block[{u}, u = (1 + х)^2; u = Expand[u] ] g[а + b]

l+2a+a2+2b+2ab+b2

u

u

u = 123456; g[2]

9

u

123456

Обратите внимание: последние действия показывают, что переменная и, введенная в тело базовой структуры, является действительно локальной переменной, и присвоение ей символьного выражения (1 + х) ^ 2 в теле блока игнорируется вне этого блока. Если переменная и до применения в функции была не определена, то она так и остается неопределенной. А если она имела до этого некоторое значение (в нашем случае — 123 456), то и по выходе из процедуры она будет иметь это значение.

Организация циклов

Многие задачи в системе Mathematica решаются с использованием линейных алгоритмов и программ. Они могут быть представлены непрерывной цепочкой выражений, выполняемых последовательно от начала до конца.

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

Циклы типа Do

К важнейшим управляющим структурам в языках программирования относятся циклы. С их помощью осуществляется циклическое исполнение некоторого выражения ехрr заданное число раз. Это число нередко определяется значением некоторой управляющей переменной (например, i, j и т. д.), меняющейся либо с шагом +1, либо от начального значения imin до конечного значения imax с шагом di. Циклы могут быть одинарными или множественными — вложенными друг в друга. Последние используют ряд управляющих переменных. Такого рода циклы организуются с помощью функции Do: О Do [expr, {imax} ] — выполняет imax раз вычисление ехрг; О Do [expr, {i, imax}] — вычисляет ехрг с переменной i, последовательно принимающей значения от 1 до imax (с шагом 1);

  • Do [expr, {i, imin, imax} ]—вычисляет ехрr с переменной i, последовательно принимающей значения от imin до imax с шагом 1;
  • Do [expr, {i, imin, imax, di}] — вычисляет ехрг с переменной i, последовательно принимающей значения от 1 до imax с шагом di;
  • Do [expr, {i, imin, imax}, {j, jmin, j max},...] — вычисляет expr, организуя ряд вложенных циклов с управляющими переменными j, i и т. д.
Примеры организации цикла Do и его исполнения представлены ниже:

Do[Print["hello"], {5}]

hello

hello

hello

hello

hello

Do[Print[i], {i, 3}]

1

2

3

Do[Print[i], {i, 5, 8}]

5

6

7

8

Do[Print[i], {i, 0 , 1, 0.25}]

0

0.25

0.5

0.75

1.

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

i=2

2

Do[Print[i], i, 1, 5]

1

2

3

4

5

1

2

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

Do [Do [Print [i, " ", j, " ", i + j], {j, 1, 3}], {i, 1, 3}];

1 1 2

1 2 3

1 3 4

2 1 3

2 2 4

2 3 5

3 1 4

3 2 5

3 3 6

Здесь используются два цикла с управляющими переменными i и j. Командой Print выводятся значения переменных i и j, а также их суммы i+j.

Следующий пример показывает применение цикла Do для задания функции, вычисляющей п-е число Фибоначчи:

fibonacci[(n_Integer)?Positive] :=

Module[fnl = 1, fn2 =0,

Do[fnl, fn2 = fnl + fn2, fnl, n- 1] ; fnl]

fibonacci[10]

55

fibonacci[100]

354224848179261915075

fibonacci[-10]

fibonacci[-10]

Обратите внимание на применение в этом примере функции Module. Она создает программный модуль с локальными переменными (в нашем случае fnl и fп2), в котором организовано рекуррентное вычисление чисел Фибоначчи.

Наконец, последний пример показывает применение цикла Do для создания цепной дроби:

х = у; Do[x = 1/(1 + k х), {k, 2, 8, 2}]; х

Циклы типа For

Другой вид цикла — цикл For — реализуется одноименной функцией:

For[start, test, incr, body]

В ней сначала один раз вычисляется выражение start, а затем поочередно вычисляются выражения body и incr до тех пор, пока условие test не перестанет давать логическое значение True. Когда это случится, то есть когда test даст False, цикл заканчивается.

Следующий пример показывает создание простой программы с циклом For и результат ее выполнения:

Print["i x"]

For [x=0; i=0, i < 4, i++

[x += 5*i, Print[i, " ", x]]]

i x

15 ,

2 15

3 30

4 50

Return[x]

Return[50]

Программа, приведенная выше, позволяет наблюдать за изменением значений управляющей переменной цикла i и переменной х, получающей за каждый цикл приращение, равное 5*i. В конце документа показан пример на использование функции возврата значений Return [x]. В цикле For не предусмотрено задание локальных переменных, так что надо следить за назначением переменных — при использовании глобальных переменных неизбежны побочные эффекты.

Циклы типа While

Итак, функция For позволяет создавать циклы, которые завершаются при выполнении (эволюции) какого-либо условия. Такие циклы можно организовать и с помощью функции While [test, expr], которая выполняет expr до тех пор, пока test не перестанет давать логическое значение True.

Ниже дан практический пример организации и использования цикла While:

i := 1; х := 1; Print["i x"] ;

While[i < 5, i += 1; x += 2*i; Print[i, " ", N[x]]]

i x

2 5.

3 11.

4 19.

5 29.

Return[x]

Return[29]

Циклы типа While, в принципе, могут заменить другие, рассмотренные выше, типы циклов. Однако это усложняет запись и понимание программ. Аппарат локальных переменных в этом типе циклов не используется.

Директивы-функции прерывания и продолжения циклов

В указанных типах циклов и в иных управляющих структурах можно использовать следующие директивы-функции:

  • Abort [ ] — вызывает прекращение вычислений с сообщением $ Aborted;
  • Break [ ] — выполняет выход из тела цикла или уровня вложенности программы, содержащего данный оператор (циклы типа Do, For и While или тело оператора-переключателя Switch). Оператор возвращает Null-значение (без генерации секции выхода);
  • Continue [ ] — задает переход на следующий шаг текущего цикла Do, For или While;
  • Interrupt [ ] — прерывает вычисления с возможностью их возобновления;
  • Return [ ] — прерывает выполнение с возвратом значения Null;
  • Return [expr] — прерывает выполнение с выводом значения выражения ехрr;
  • Throw [value] — задает прекращение выполнения цикла Catch, если в ходе эволюции ехрг встречается значение value (см. примеры выше).
На рис. 10.4 представлено применение директив Abort [ ] и Interrupt [ ] в середине набора команд. Нетрудно заметить, что директива Abort [ ] просто прерывает выполнение цепочки команд и выводит сообщение $ Aborted. А вот директива Interrupt [ ] выводит диалоговое окно, с помощью которого можно либо прервать вычисления, либо продолжить их.

Рис. 10.4. Действие директив Abort[] и lnterrupt[]

Если продолжить вычисления (нажав кнопку Continue Evaluation), то вывод выражений командами Print будет продолжен, что видно из рис. 10.5.

Условные выражения и безусловные переходы

Для подготовки полноценных программ помимо средств организации циклов необходимы и средства для создания разветвляющихся программ произвольной структуры. Обычно они реализуются с помощью условных выражений, позволяющих в зависимости от выполнения или невыполнения некоторого условия (condition) выполнять те или иные фрагменты программ.

Рис. 10..5. Продолжение вычислений после команды Interrupt[]

Функция IF

Как у большинства языков программирования, условные выражения задаются с помощью оператора или функции IF. Система Mathematica имеет функцию If, формы которой представлены ниже:

  • If [condition, t, f] — возвращает t, если результатом вычисления condition является True, и f, если результат равен False;
  • If [condition, t, f, u ]—то же, но дает и, если в результате вычисления condition не было получено ни True, ни False.
Следующий пример показывает создание программной процедуры с циклом Do, выход из которой реализуется с помощью функции I f и директивы прерывания Aborted! ]:

х := 1; Print["i x"];

Do[{If [i == 5, Abort[], None],

i += 1; x += 2*i; Print[i, " ", N[x]]},

{i, 1, 100}]

i x

2 5

3 11.

4 19.

5 29.

$Aborted

Return[x]

Return[1]

Тот же пример, но с применением директивы выхода из цикла Break [] в функции If показан ниже:

х := 1; Print["i x"];

Do[{If [i == 5, Break[], None],

i += 1; x += 2*i; Print[i, " ", N[x]]},

{i, 1, 100}]

i x

2 5.

3 11.

4 19.

5 29.

Return[x]

Return[29]

В данном случае никаких специальных сообщений о выходе из цикла не выдается. Функция If обеспечивает ветвление максимум по двум ветвям программы. Для ветвления по многим направлениям можно использовать древовидные структуры программ с множеством функций If. Однако это усложняет исходный текст программы.

Функции-переключатели

Для организации ветвления по многим направлениям в современных языках программирования используются операторы-переключатели. В системе Mathematica множественное ветвление организовано с помощью функций Which и Switch:

  • Which [testl, valuel, test2, value2,...] — вычисляет в порядке следования каждый из testi, сразу возвращая именно ту величину из valuei, которая относится к первому testi, давшему True;
  • Switch [expr, forml, valuel, form2, value2,...] — вычисляет селектор expr, затем сравнивает его последовательно с каждой из меток f ormi, вычисляя и возвращая то valuei, которое соответствует первому совпадению.
Приведем примеры работы функции which:

Whichtl == 2,1,2== 2, 2, 3 == 3, 3]

2

Which[l == 2, x, 2 == 2, у, 3 == 3, z]

y

Следующие примеры иллюстрируют работу функции Switch:

Switch[1, 1, а, 2, b, 3, с]

а

Switch[2, 1, а, 2, b, 3, с]

b

Switch[3, 1, а, 2, b, 3, с]

с

Switch[8, 1, а, 2, b, 3, с]

Switch[8,

1, а,

2, b,

3, с]

Обратите внимание на последний пример — при неверном задании первого параметра (селектора) просто повторяется запись функции.

Следующий пример показывает возможность выбора с применением вещественных значений селектора и меток:

Switch[8., 1.5, а, 2.5, b, 8., с]

с

Switch[1.5, 1.5, а, 2.5, b, 8., с]

а

Switch[8, 1.5, а, 2.5, b, 8., с]

Switch[8,

1.5, а,

2.5, b,

8., с]

Опять-таки, обратите внимание на последний пример — здесь использован селектор в виде целого числа 8, тогда как метка выбора — вещественное число 8. Выбор при этом не происходит, поскольку целочисленное значение 8 не является тождественным вещественной восьмерке.

Безусловные переходы

В целом, условные выражения в языке программирования системы Mathematica позволяют реализовать любой вид ветвления в программах. Однако иногда бывает полезно без лишних раздумий указать в программе явный переход к какой-либо ее части. Для этого используется оператор безусловного перехода Goto [tag]. который дает переход к тому месту программы, которое отмечено меткой Label [tag]. Возможны также формы Goto [expr] и Label [expr], где ехрr — вычисляемое выражение.

Применение оператора Goto иллюстрирует следующий пример:

(q = 2; Label[start]; Print[q]; q += 2;

If[q < 7, Goto[start]])

2

4

6

Здесь с помощью оператора Goto [start] организован цикл с возвратом на метку Label [start], действующий до тех пор, пока значение q меньше 7. При этом q меняется от начального значения 2 с шагом 2, причем добавление 2 к текущему значению q осуществляется укороченным оператором сложения q+=2.

Интересной особенностью языка программирования Mathematica является возможность создания переходов по значению вычисляемого выражения. Например, Goto [2+3] дает переход к метке Label [5] или даже Label [1+4], что видно из следующего примера:

Goto[2 + 3];

Print["ааааа"];

Label[1 + 4];

Print["bbbbb"]

bbbbb

Переходы, задаваемые выражениями, и метки, меняющие свой идентификатор, редко встречаются в обычных языках программирования, хотя они обеспечивают новые обширные и довольно необычные возможности по созданию программ с различными ветвлениями.

Для языка программирования системы Mathematica, ориентированного на безупречное и строгое структурное программирование, введение оператора Goto может расцениваться как отступничество от основополагающих идей структурного программирования. Поэтому на применение этого оператора в методах структурного программирования наложено табу. Тем не менее, этот оператор есть, а применять его или нет — дело пользователя.

 

Содержание раздела