Язык программирования Mathcad.
Взгляд со стороны

(статья-remake)

В.Очков

"Pergam turbare porro: ita haec res postulat"

"Буду продолжать свои выдумки - этого требует дело"

(Плавт. "Привидение", пер. с. лат.)

В конце 1995 г. на российском рынке появилась новая разработка фирмы MathSoft - шестая версия программы Mathcad (Mathcad PLUS 6.0).

Наиболее заметная “изюминка” этой версии, которую сразу оценили многочисленные пользователи Mathcad, – это встроенный язык программирования. Попробуем взглянуть на него со стороны традиционных языков программирования.

1. Немного истории

При работе с Mathcad всегда была потребность в программировании для расширения и совершенствования базового набора математических инструментов. Конкретный пример. В Mathcad есть функция root, возвращающая корень алгебраического уравнения. Она использует метод секущих. Но есть уравнения, корень которых разумнее искать другими методами - методом Ньютона (касательных), методом половинного деления (бисекции) и т.д. Кроме того, стандартные методы Mathcad поиска корня и оптимума уравнений (функции root и minerr), решения систем алгебраических уравнений (функция find) ориентированы по точности не на значение аргумента, а на значение функции, что также нередко мешает поиску результата расчета.

Эта потребность в программировании в среде Mathcad реализовалась более-менее опытными пользователями тремя путями:

Путь 1. В самых ранних версиях Mathcad были две функции if и until, позволяющие через различные хитрости и трюки реализовывать две основные алгоритмические конструкции - выбор и повторение. Но эти функции не могут иметь в качестве аргументов блок составных операторов. Поэтому для реализации даже несложного алгоритма требовалось подключать механизм вложенных функций, что нередко превращало программу в криптограмму, в которой потом даже ее автор разбирался с трудом.

Путь 2. Версии Mathcad для Windows - это полноценные Windows-приложения. Решая в среде Mathcad конкретную задачу, можно в статике (через Буфер Обменов) или в динамике (технологии DDE и OLE) перенести скаляр, вектор или матрицу в среду, например, языка fortran и решить задачу (этап задачи).

Путь 3. Начиная с пятой версии Mathcad пользователям была представлена возможность объявления в среде Mathcad новых встроенных функций. Их нужно было написать на языке С, откомпилировать каким-либо 32-разрядным транслятором и прикрепить к Mathcad через механизм DLL. Но этот путь (как, кстати, и второй) с самого начала был тупиковым. Во-первых, Mathcad создавался как инструмент решения широкого класса задач теми пользователями, кто не хотел или не умел программировать. При обращении же к языку C получалось так, что от чего ушли, к тому пришли. Во-вторых, если кто-то и переключался от работы с Mathcad к работе с языком С, то он, как правило, там и оставался, программируя целиком всю задачу. В-третьих (вернее, во-вторых с половиной), если кто-то мог на С решить свою задачу, то он обычно услугами Mathcad и не пользовался, считая это ниже своего достоинства.

2. Семерка кнопок

При первом взгляде на Mathcad PLUS 6.0 возникает удивление - почему в него был встроен не какой-либо известный и широко используемый язык, а разработан новый, ни на что не похожий. Электронные таблицы Excel 5.0, например, используют встроенный BASIC [1], и это кажется естественным даже для самых непримиримых противников этого языка. Но после детального знакомства с языком Mathcad удивление сменяется пониманием и даже удовлетворением. Становится ясным, что в рамки традиционных языков с программами в текстовом формате невозможно втиснуть богатый набор инструментария Mathcad, который реализован не только и не сколько встроенными функциями, но и в общепринятом математическом виде.

В Mathcad по сути не встроен язык программирования, а просто снято вышеотмеченное ограничение на использование составных операторов в теле алгоритмических управляющих конструкций выбор и повторение. Кроме того, добавлен цикл с параметром и оператор досрочного выхода break, о котором подробно будет рассказано ниже (часть 7 - remake).

Алгоритмические конструкции и составные операторы в среде Mathcad вводятся не традиционным набором через клавиатуру ключевых слов IF, THEN, ELSE, WHILE и т.д., а нажимом одной из семи кнопок панели программирования:

Add Line

¬

if

while

for

break

otherwise

 

Опишем их.

Add Line - добавить строку в программу, в тело цикла, в плечо альтернативы и т.д. Этим действием снимается ограничение на число операторов во вложенных конструкциях языка.

¬ - знак присвоения. На Pascalў e мы пишем А := B + C, на BASICў е А= В + С. В языке же Mathcad А ¬ B+ С. Почему? Сначала опять приходится недоумевать, но потом понимаешь, что без знака ¬ программа превращалась бы в нечто невразумительное, бьющее в глаза программисту:

A := A := B + C

В выражении же

A := A ¬ B + C

все ясно: локальной переменной A присваивается (¬ ) значение суммы двух глобальных переменных B и C, значение которых где-то уже задано в документе Mathcad. Затем эта сумма передается (:=)глобальной переменной A.

while. Нажав на эту кнопку, мы получим на экране заготовку цикла с предпроверкой - слово while с двумя пустыми квадратиками:

В первый квадратик (правее while) нужно будет записать булевое выражение (переменную), управляющее циклом, а во второй (ниже while) - тело цикла. Если в теле цикла более одного оператора, то нужно воспользоваться уже описанной кнопкой Add Line.

if. Эта кнопка позволяет вводить в программу альтернативу с одним плечом. Pascal-конструкция if A > B then C := D, в среде Mathcad будет записана несколько по-еврейски: С ¬ D if A > B. Почему?! Хозяин-барин.

otherwise. Эта кнопка превращает неполную альтернативу в полную.

Pascal:

if A > B then C := D else E := F;

Mathcad:

C ¬ D if A > B

 

E ¬ F otherwise

Слово otherwise хоть и является синонимом слова else, но оператор otherwise не совсем одно и то же, что и ключевое слово else языка Pascal. В среде Mathcad можно написать

А := 1+2 otherwise

и сообщения об ошибке не будет. В традиционных же алгоритмических языках ключевое слово else может стоять только в связке с if и then. В Mathcad же это не обязательно.

for. Эта кнопка позволяет вводить в программы цикл с параметром, который имеет некоторые особенности, отличающие его от аналогов в языках Pascal и BASIC. В языке Pascal цикл с параметром может иметь либо единичный (to), либо минус единичный (downto) шаг. На языке BASIC шаг может быть любым (step ...); если он не указан, то он по умолчанию единичный. Если в среде Mathcad шаг в цикле с параметром не указан, то он может быть равен либо единице

for i О 1 .. 10

либо минус единице

for i О 10 .. 1

Аналоги цикла с параметром среды Mathcad следует искать не в языках Pascal и BASIC, а в языке C.

break. Кнопка досрочного выхода из программы или цикла. О ней разговор особый.

Ниже приведены примеры программ в среде Mathcad.

Общие замечания.

Язык программирования Mathcad по своей идеологии очень похож на язык FRED интегрированного пакета Framework. Говорят, что один из погорельцев фирмы Ashton-Tate (разработчик Framework) перешел в фирму MathSoft и приложил руку к созданию языка программирования Mathcad. Внешне же своими вертикальными линиями, фиксирующими начало, конец и глубину вложений конструкций программы, Mathcad напоминает алгоритмические конструкции книги А.П.Брудно “Программирование в содержательных обозначениях” (М.: ”Наука”, 1968). Автор этой статьи в также увлекался подобными линиями, втискивая программы в рамки структурных диаграмм (см., например, книгу “128 советов начинающему программисту” М.: Энергоатомиздат, 1991).

Линии программ Mathcad более наглядны (особенно для обучения структурному программированию), чем операторные скобки традиционных языков - begin-end на языке Pascal, фигурные скобки на языке С, оператор list на языке FRED и т.д.

3. Программа-константа, программа-переменная, программа-функция

Автор в свое время [2] сетовал на то, что в среде Mathcad нельзя группу формул заключить в рамки цикла, чтобы можно было, например, реализовать метод последовательных приближений в автоматическом режиме, без ручного переноса значения последнего приближения в голову расчета. Хоть в Mathcad и введен язык программирования с циклами, но положения статьи [2] остаются в силе: программирование в Mathcad ограничивается либо формированием сложной формулы для расчета переменной (константы), которую потом можно вставлять в выражения (программа 1), либо заданием функции пользователя (программа 2). Третья возможность (см. сноску 3) - это использование программирования для формирования оператора пользователя. Но, как понимает читатель, функция отличается от оператора лишь внешне: можно написать add(A, B), но лучше A + B. Mathcad тем и хорош, что позволяет записывать задачу в общепринятой математической нотации.

Программа 1

Программа 2

4. Скаляр - вектор - матрица

Эта тройка (опять смотри сноску 3) переменных может задаваться и программно. Формирование скаляра уже проиллюстрировано программами 1 и 2. Программы 3 и 4 формируют вектор и матрицу, соответственно. Функция - это переменная, заготовленная впрок: в среде Mathcad можно формировать не только простые, но и индексные (в виде вектора или матрицы) функции: y(x)0, y(x)1, y(x)2 и т.д.

Программа 3

Программа 4

Переменные в Mathcad теперь могут быть локальными, самообъявляющимися в программах (как, например, переменные i и j в программе 4) и пропадающими по выходу из нее. Кроме локальной и глобальной переменной, значение которой задано вне программы и автоматически проникающее в нее, в среде Mathcad есть и системные, (предопределенные) переменные и константы. Пример - число e или p , значение которых определено самой системой (математикой), а не пользователем. Бог любит троицу - см. сноску 3.

5. Рекурсия

Языки программирования обычно проходят три стадии:

1. Рекурсия невозможна

2. Рекурсия неразрешена, но применяется по принципу “Если нельзя, но очень хочется, то можно”

3. Рекурсия разрешена

Mathcad вторую стадию успешно проскочил.

И вот уже трещат морозы

И серебрятся средь полей...

(Читатель ждет уж рифмы розы;

На, вот возьми ее скорей!)

Читатель ждет уж примеров рекурсии в среде Mathcad? Скорее всего нет. Они ему оскомину набили. Но мы попробуем чего-нибудь свеженького. Например, ретроспективную рекурсию.

Как запомнить число e (основание натурального логарифма) c десятью цифрами после запятой? Очень просто: две целых семь десятых плюс два раза Лев Толстой - 2.718281828, то есть к числу 2.7 нужно прибавить два раза год рождения писателя. Остается только самая малость - запомнить, в каком году родился Лев Толстой, или, на худой конец, сообразить, что это случилось в прошлом веке, чтобы вспомнить хотя бы три знака числа e после запятой: 2.718. Ретроспектива, обращенная в 19-й век, поможет запомнить довольно-таки точное значение одной из фундаментальных констант математики.

Как запомнить, что факториал нуля равен не нулю (типичная ошибка), а единице? Очень просто. Нужно применить ретроспективный метод поиска факториала числа: сообщить машине факториал какого-либо положительного числа N (5!=120, например) и то, что факториал предыдущего числа N-1 равен факториалу первого (N!), разделенному на N. Факториал пяти (120) - это такая же тривиальная истина, как и то, что Лев Толстой родился в девятнадцатом веке.

В среде Mathcad, работая по программе 5, можно не только правильно определить факториал нуля (единица), но и получить факториалы отрицательных чисел. Машину нужно только обучить этому - заложить в программу ретроспективную рекурсию для поиска факториала на всем целочисленном диапазоне.

Программа 5

В 1202 году Леонардо Пизанский (1180-1240) описал одну из самых первых моделей развития замкнутой биологической системы, населенной условными кроликами. Если соответствующим образом определить их плодовитость и долголетие, то численность популяции кроликов будет меняться из поколения в поколение по строгому закону:

Поколение

...

4

5

6

7

8

9

10

11

...

Число кроликов

...

3

5

8

13

21

34

55

89

...

Читатель, конечно, уже догадался, что речь пойдет о числах Фибоначчи - Леонардо Пизанский более известен под именем Фибоначчи (Fibonacci - сокращение от filius Bonacci - сын Боначчи). Приведенный ряд специально начат не с традиционного места (первое поколение), а с четвертого поколения для того, чтобы задать читателю вопрос, подобный тому, какой стоял в задаче о факториале: "Чему равно минимальное число кроликов в популяции - каково наименьшее число Фибоначчи?" Нормальный ответ, приводимый во всех учебниках, - ноль. Но не будем спешить и напишем программу 6 с двухсторонней рекурсией, взяв за базовые числа Фибоначчи не традиционную пару 0 и 1, а 3 и 5.

Программа 6

Про кроликов Фибоначчи со временем забыли, но числа Фибоначчи в наше время широко применяются в вычислительной математике для иллюстрации рекурсии как, например, в нашей статье.

6. Размерность

Программа по знаменитой формуле Н.Вирта - это сумма алгоритма и структуры данных. Алгоритм программы в среде Mathcad задается нажатием на клавиатуру из семи кнопок (см. выше), отвечающих за циклы и альтернативы. Структура данных в среде Mathcad также проста. Там нет ни текстовых, ни булевых, ни других экзотических переменных. Есть только числовые переменные двойной точности, которые предстают перед пользователем в комплексном, вещественном или целочисленном виде. Они могут группироваться в векторы и в матрицы. Булевые переменные в среде Mathcad, как и в языке BASIC, хранят целочисленные значения 1 (Да, Thrue) или 0 (Нет, False):

1 >2 = 0

1 < 2 = 1 и т.д.

Тип переменной в среде Mathcad в какой-то мере заменяется понятием размерности хранимой величины, что очень удобно в инженерных расчетах. По программе 7 рассчитывается площадь треугольника формированием функциии пользователя с именем Площ_треуг и вызовом ее. Аргументы функции Площ_треуг могут быть величины с разной размерностью длины (метры- m, сантиметры -cm, дюймы - in и т.д.). Система Mathcad сама в них разберется, сделает нужные пересчеты и выдаст правильный результат в выбранной пользователем системе единиц (кг-м-секунда, г-см-секунда, британская и т.д.).

Программа 7

Система Mathcad ведет контроль размерностей и не позволяет складывать, например, метры с килограммами.

Решая в среде Mathcad задачу, можно отвлечься от конкретных размерностей (кг, м, с и т.д.), но, тем не менее, вести контроль размерностей, дополняя ввод значения переменной указанием к какой физической величине она относится (масса, длина, время и т.д.) :

m:=10M a:=2L t:=5T и т.д.

Переменные M, L и T, а также G и K можно считать системными (предопределенными), хранящими единичные значения основных физических величин (G - заряд, K- абсолютная температура, остальное - см. выше). Этой компании не хватает количества вещества и силы света, чтоб превратиться в семерку (см. сноску 3).

7. Ramake

Старая песня на новый лад. Так можно перевести английское слово remake. Прием remake особо популярен в кинематографе. Берется старый фильм, вернее, старый уже отснятый сценарий, по которому снимается новая версия кинокартины. Технология remake применяется и в программировании, когда, например, DOS-овская версия какой-либо программы переписывается для Windows.

В седьмой части статьи автор в стиле remake расскажет о недостатках языка Mathcad.

Почему о недостатках?!

О достоинствах итак было и будет еще сказано немало.

Почему remake?!

Во-первых, у автора сохранился файл с текстом статьи “Turbo Pascal 7.0. Взгляд со стороны” (КомпьютерПресс, N 7, 1993), которая и подвергается remake’у. Во-вторых, язык Mathcad как и язык Pascal оказался окутан паутиной старых догм структурного программирования, о которых писалось в первой статье. Это делает remake не только простым, но и уместным и естественным.

Итак, remake.

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

' Программа 8. Метод Ньютона-1 (QBasic)

DEF FNy (x) = x ^ 2 - 3 ’ функция

DEF FNDy (x) = 2 * x ’ ее производная

TOL = .001

INPUT "X начальн."; x

DO

x1 = x - FNy(x) / FNDy(x)

IF ABS(x1 - x) <= TOL THEN EXIT DO

x = x1

LOOP

PRINT "Y=0 при X ="; x

END

На листинге 8 приведена QBasic-программа поиска корня алгебраического уравнения методом Ньютона (касательных). Почему мы начали с QBasic'а, хотя статья посвящена языку Mathcad. Дело в том, что язык QBasic кроме традиционной тройки циклов (цикл с предпроверкой, цикл с постпроверкой, цикл с параметром) имеет и цикл с выходом из середины: DO [...] IF ... THEN [...] EXIT DO [...] LOOP. Эта конструкция, наряду с другими преимуществами, о которых будет сказано ниже, позволяет реализовывать алгоритмы в их естественной последовательности. Так в программе 8 объявляются и создаются функции пользователя (анализируемое уравнение y и его производная Dy), запрашивается значение начального приближения к корню x и задается значение погрешности TOL. После этого организуется цикл, но не традиционный, а с выходом из середины. В цикле, следуя естественному порядку алгоритма Ньютона, рассчитывается новое приближение к корню (x1) и, если оно отстает от предыдущего не более чем на величину заданной погрешности, то задача считается решенной (EXIT DO). Если нет, то ведется подготовка к новому приближению (x = x1), а цикл повторяется (LOOP).

При реализации на языке Mathcad этот несложный алгоритм обрастает "архитектурными излишествами", т.к. его приходится запихивать в прокрустово ложе цикла while - см. листинг 9.

Программа 9

Цикл с предпроверкой (while) требует, чтобы булевое выражение заголовка было определено еще до входа в цикл, а этого нет при поиске корня методом Ньютона. Для таких ситуаций предусмотрен цикл с постпроверкой, а его нет в языке Mathcad. Приходится до входа (и для входа) в цикл писать x1 ¬ x+2· TOL. Подобным образом лгут детям (а машина - тоже дитя), заставляя их что-то делать. Строку x1 ¬ x+2· TOL можно уподобить стартеру двигателя внутреннего сгорания, работающего, кстати, как и программа 9 циклически. Вот какими аллегориями - дитя, двигатель - обросла наша простенькая программа из-за того, что на языке Mathcad нет цикла с выходом из середины.

Cтроку

x1¬ x+2 Ч TOL

обычно заменяют на строку

x1 ¬

но это тоже не оптимальное решение: “Не упоминай имя Божье и не вызывай функцию всуе “.

Можно отметить и другую ненатуральность программы 9 - постановку телеги впереди лошади: в цикле сначала приходится готовиться к новому приближению (x1 ¬ x), хотя еще не ясно, понадобиться оно или нет, а только потом проводить его.

Конструкция break, введенная в Mathcad, призвана вернуть программе 2 ее естественность, но...

Программа 10

QBasic-конструкция DO ... LOOP (см. листинг 8) на листинге 10 превратилась в конструкцию while 1 ..., которую на латинский язык можно перевести как "ad calendas greaces" - "до греческих календ". Пришлось в программе 10 как и в программе 9 идти ну не на обман, так на натяжку: "выполняй, пока рак на горе не свистнет".

История с вводом в Mathcad конструкции break подтверждает старую истину о том, что "нет ничего практичнее хорошей теории". И вот почему.

Вышеприведенный анализ циклов на языке Mathcad имеет не только сугубо практический, но и чисто теоретический аспект. Как известно, набор управляющих конструкций любого структурного языка ведет свою родословную от основной структурной теоремы Э.Дейкстры, объявившей войну меткам: "Алгоритм любой сложности можно реализовать, используя только цикл while и альтернативу". Автор затратил уйму времени и сил в поисках статьи или книги с доказательством этой теоремы, но так и не нашел его. А вот показать, что эта теорема не верна, можно в два счета - см. листинги 11 и 12. Эти программы решают уже известную нам задачу о корне алгебраического уравнения, но другим методом - методом половинного деления. Его алгоритм - это простейшая иллюстрация теоремы Дейкстры: цикл (while) приближения к корню с вложенной альтернативой (... if ... otherwise). Если корень правее центра интервала x1 - x2, то к нему (к центру) подтягивается левый край (x1¬ x), нет - правый (x2¬ x). В программе 12 альтернатива программы 11 заменена на два цикла while, операторы тела которых попеременно выполняются либо раз, либо ни разу, что регулируется переменной Flag.

Программа 11

Программа 12

В аналогичной QBasic-программе (листинг 13) также обошлись без альтернативы. Более того, удалось избавиться и от переменной-диспетчера Flag, заменив два цикла while на один цикл, но с двумя выходами из середины - с одним условным, а вторым безусловным.

' Программа 13. Метод половинного деления-3 (QBasic)

DEF FNy (x) = x ^ 2 - 3 ‘ функция

TOL = .001

INPUT "X1, X2"; x1, x2

DO

IF ABS(x1 - x2) <= TOL THEN EXIT DO

x = (x1 + x2) / 2

DO

IF SGN(FNy(x1)) = SGN(FNy(x)) THEN x1 = x: EXIT DO

x2 = x: EXIT DO

LOOP

LOOP ' Конец цикла

PRINT "Y = 0 при X ="; x

END

Весь фокус программ 12 и 13 в том, что альтернатива - это средство ускоренного "путешествия" по алгоритму только в одну сторону (сверху вниз и слева направо, если смотреть на листинг), а цикл - в обе. Отсюда и ненужность (в теоретическом плане, конечно, а не в практическом) альтернативы. Цикл DO [...] IF ... THEN [...]EXIT DO [...] LOOP можно считать гибридом цикла и альтернативы.

Доказательством теоремы Э.Дейкстры может служить факт, что до сих пор не было случая, когда задуманный алгоритм нельзя было бы реализовать, используя только цикл и альтернативу. Если альтернативу исключить, то основная структурная теорема должна звучать так: "Алгоритм любой сложности можно реализовать, используя только циклы (цикл repeat ... if ... then ... break; [if ... then ... break;],,, ... until)". Вот это-то теоретическое положение вводом процедуры break и подсовывают задним числом языки программирования Mathcad и Pascal под свой фундамент. Обращаю внимание на несовершенную форму глагола - "подсовывают", а не "подсунули" - цикл с выходом из середины на языке Mathcad осуществим через насилие над циклом while (листинг 3).

Теорему Э.Дейкстры следует "понизить в звании" и называть леммой, т.е. вспомогательной теоремой, служащей для доказательства основной.

Здесь мы вернулись к спорам, бушевавшим лет 30 назад. Процедура break, введенная в язык Mathcad, дала нам повод напомнить о них.

Вернемся к практическим заботам.

Пользователям Mathcad, кодируя даже несложную программу, все время придется волей-неволей доказывать основную структурную теорему - втискивать свой алгоритм в узкие рамки цикла while и альтернативы. Непонятно, почему в Mathcad ввели цикл for, который несложно заменить тем же циклом while. По-видимому, для того, чтобы кнопок на панели программирования было ровно семь. Программисту очень не будет хватать цикла с постпроверкой, расширенного множественного ветвления и даже метки [3]. И все из-за того, что разработчики Mathcad ориентируются на принципы программирования двадцатилетней давности - теорема Дейкстры всесильна потому, что она верна.

Литература:

  1. Очков В. Excel 5.0: заметки на полях. КомпьютерПресс, N 4, 1995
  2. Очков В. Сказ про то, как Mathcad задачу решал. КомпьютерПресс, N N 1 и 2, 1995
  3. Очков В. 7 этюдов в среде Mathcad. Издательство КомпьютерПресс, 1996