Вопрос: Какой язык программирования вы предпочитаете
|
Иллюстрированный самоучитель по Mathematica
Использование процедур
В основе
процедурного программирования лежит понятие процедуры
и типовых средств
управления — циклов, условных и безусловных выражений и т. д. Процедурный подход
— самый распространенный в программировании, и разработчики 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 может расцениваться как
отступничество от основополагающих идей структурного программирования. Поэтому
на применение этого оператора в методах структурного программирования наложено
табу. Тем не менее, этот оператор есть, а применять его или нет — дело пользователя.
|
|
|