Введение в Yacas. Учебник и примеры.

Перевод Андрея В. Зорина, 2002


Этот документ дает краткое введение в Yacas. Включен краткий учебник по синтаксису и некоторым командам, чтобы позволить вам начать использовать Yacas. Есть также несколько примеров.


Как начать работу с Yacas

Введение.
Yacas (Yet Another Computer Algebra System, Еще Одна Система Компьютерной Алгебры) - это маленькая и очень гибкая система компьютерной алгебры общего назначения и язык программирования. Язык имеет знакомый Си-подобный инфиксно-операторный синтаксис. Дистрибутив содержит небольшую библиотеку математических функций, но ее настоящая мощь в языке, на котором вы можете легко писать ваши собственные алгоритмы символьных манипуляций. Ядро поддерживает арифметику с произвольной точностью (для более быстрых вычислений оно может быть по выбору слинковано с библиотекой математики с произвольной точностью GNU libgmp) и способно выполнять символьные манипуляции над различными математическими объектами, следуя определенным пользователем правилам.

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

Установка Yacas.
Прочитайте файл INSTALL для инструкций по компиляции Yacas'а. Yacas переносим на большинство Unix'овых платформ и требует только стандартный компилятор C++, такой как g++.

Базовое приложение Yacas принимает текст на входе и возвращает текст на выходе. Это делает его достаточно платформо-независимым. Кроме Unix-подобных систем Yacas был откомпилирован под Windows и на EPOC32, известном также как Psion (который не поставляется со стандартной библиотекой C++!). Исходный код для компиляции Yacas под Windows можно найти в хранилище Sourceforge.

Для Unix компиляция обычно выливается в стандартную последовательность:
./configure
make
make install

Это установит бинарники в /usr/local/bin и библиотечные файлы в /usr/local/share/yacas/.

Компиляция с libgmp
В общем случае математика с произвольной точностью в Yacas будет быстрее, если вы компилируете Yacas с библиотекой libgmp (опция --enable-gmp для скрипта configure). Также имеются прекомпилированные пакеты Red Hat (RPM) и Debian (DEB).

Получение печатного руководства
Дополнительно, отформатированная в LaTeX документация в форматах PDF и PostScript может быть сделана командой
make texdocs
Использование консольного режима.
Вы можете запустить Yacas в консольном режиме просто напечатав yacas. Командное приглашение Yacas выглядит так:
In>
а ответ Yacas'а появляется после приглашения
Out>
Сессия Yacas'а можно прервать, напечатав Exit() или quit. Нажатие ^C также приведет к выходу из Yacas'а. Однако нажатие ^C в то время, когда Yacas занят вычислениями, остановит только вычисления. Сессия может быть перезапущена (забывая все предыдущие определения и результаты) печатью
restart
Обычно вы будете вводить по одному выражению на строчку, например
In> Sin(Pi/2);
Out> 1;
Выражения должны бы оканчиваться точкой с запятой (;), хотя это и не требуется (Yacas добавит точку с запятой в конце строки, чтобы завершить выражение).

Вся документация доступна через приглашение Yacas. Если вы напечатаете
In> ??
вы сможете прочитать все имеющиеся руководства; Yacas запустит lynx или другой браузер, чтобы показать вам html-документацию. Вы можете также получить помощь по конкретной функции: чтобы прочитать о функции Sum(), напечатайте
In> ?Sum
Напечатайте Example();, чтобы получить случайные примеры вычислений в Yacas.

Командная строка имеет список истории, так что должно быть легко путешествовать через выражения, которые вы раньше ввели, используя клавиши Стрелка вверх и Стрелка вниз. Напечатав первые несколько символов предыдущего выражения и затем нажав клавишу TAB, вы заставите Yacas вспомнить последнее выражение в списке истории, которое удовлетворяет этим первым символам.

Команды, растягивающиеся на несколько строчек могут (а на самом деле и должны) быть введены с использованием завершающей обратной косой черты \ в конце каждой продолженной строки. Например:
In> a:=2+3+
Error on line 1 in file [CommandLine]
Line error occured on:
>>>
Error parsing expression

In> a:=2+3+ \
In> 1
Out> 6;

Ошибка после нашей первой попытки произошла потому что Yacas присоединил точку с запятой в конце первой строки и 2+3+; не является допустимым выражением Yacas.

Кстати, любой текст, который Yacas печатает без приглашения, есть либо сообщение, напечатанное функцией как ее побочный эффект, либо сообщение об ошибке. Результирующие значения выражений всегда печатаются после приглашения Out>.

Yacas как символьный калькулятор
Мы готовы попробовать некоторые вычисления. Yacas использует Си-подобный инфиксный синтаксис и чувствителен к регистру. Вот для начала некоторые точные манипуляции с дробями:
In> 1/14+5/21*(30-(1+1/2)*5^2);
Out> -12/7;

Стандартные скрипты уже содержат простую математическую библиотеку для символьного упрощения основных алгебраических функций. Любые имена, такие как x, рассматриваются как независимые символьные переменные и по умалчанию не оцениваются.
In> 0+x;
Out> x;
In> x+1*y;
Out> x+y;
In> Sin(ArcSin(alpha))+ArcCos(Cos(beta));
Out> alpha+beta;
In> (x+y)^3-(x-y)^3
Out> (x+y)^3-(x-y)^3;
In> Simplify(%)
Out> 6*x^2*y+2*y^3;
Специальный оператор % автоматически вызывает обратно результат от предыдущей строки. Функция Simplify пытается редуцировать выражение к более простой форме. Заметьте, что имена стандартных функций в Yacas обычно начинаются с заглавной. Временами используется множественное обзаглавливание, как например ArcSin. Знак подчеркивания _ есть зарезервированный символ оператора и не может быть частью имен переменных или функций.

Yacas может работать с числами с произвольной точностью:
In> 20!;
Out> 2432902008176640000;
Когда имеете дело с числоми с плаваюшей точкой, команда Precision(n); может быть использована, чтобы указать, что все числа с плавающей точкой должны иметь фиксированную точность из n разрядов:
In> Precision(30);
Out> True;
In> N(1/243);
Out> 0.004115226337448559670781893004;
Заметьте, что мы должны вводить N(), чтобы форсировать приближенное вычисление, иначе дробь была бы оставлена неоцененной. Значение True - это булевская константа.

Аналитические производные функций могут быть оценены:
In> D(x) Sin(x);
Out> Cos(x);
In> D(x) D(x) Sin(x);
Out> -Sin(x);
Рациональные числа останутся рациональными покуда числитель и знаменатель целые, так что (55/10) будет оценено в (11/2). Вы можете обойти это поведение используя функцию численного оценивания N(). Например, N(55/10) будет оценено в 5.5 . Это поведение имеет место для большинства математических функций. Yacas будет пытаться поддерживать точный ответ (в терминах целых чисел или дробей) вместо использования чисел с плавающей точкой, если только не используется N(). Где требуется значение константы pi, используйте встроенную переменную Pi. Она будет замещена (приближенным) числовым значением, когда будет вызвана N(Pi). Yacas знает некоторые правила упрощений с использованием Pi (особенно с тригонометрическими функциями). Мнимая единица i обозначается I и комплексные числа могут быть введены как выражения, включающие I или явно Complex(a,b) для a+ib.

Готовы некоторые простые алгоритмы для решения уравнений:
In> Solve(a+x*y==z,x);
Out> (z-a)/y;
In> Solve({11*x+3*y==1,2*x+y==0},{x,y})
Out> {{1/5,-2/5}};
(Заметьте использование оператора ==, который ни во что не оценивается, чтобы обозначить объект-"уравнение".) В настоящее время Solve достаточно ограничен и только имеет дело с уравнениями, где переменная, относительно которой их надо разрешить, встречается в уравнении только однажды. В будущем будут более сложные алгоритмы.

Ряды Тейлора поддерживаются, например:
In> Taylor(x,0,3) Exp(x)
Out> 1+x+(1/2)*x^2+(1/6)*x^3;
Поскольку такую форму ответа может оказаться тяжело читать, вы можете затем напечатать
In> PrettyForm(%);
/ 1 \ 2 / 1 \ 3
1 + x + | - | * x + | - | * x
\ 2 / \ 6 /

Out> True;
Функция PrettyForm() пытается отрисовать формулу в лучшем формате для чтения, используя текст ASCII. Вы можете также экспортировать выражения в TeX, напечатав TeXForm(...).

Переменные
Yacas поддерживает переменные:
In> Set(a,Cos(0));
Out> True;
In> a:=a+1;
Out> 2;
Переменная a теперь глобально установлена в 2. Функция Set() и оператор := оба могут быть использованы для присвоения значений глобальным переменным. (Переменные, локальные для процедур, тоже могут быть определены; смотри ниже главы о программировании.) Чтобы очистить связь переменной, выполните Clear(a); "a" теперь будет оцениваться просто в a. Это одно из свойств схемы оценивания Yacas'а: когда какой-то объект уже не может быть далее оценен или преобразован, он возвращается в качестве результата.

В настоящее время нет никакой разницы между присваиванием переменным с использованием Set() или оператора :=. Последний, однако, может присваивать списки и определять функции.

Функции
Оператор := может быть использован, чтобы определить функции:
f(x):=2*x*x
определит новую функцию, f, которая принимает один аргумент и возвращает удвоенный квадрат этого аргумента.

Одно и то же имя функции f может быть использовано различными функциями если они принимают различное количество аргументов ( но не если они принимают просто различные типы аргументов, поскольку Yacas не имеет строго системы типов) :
In> f(x):=x^2;
Out> True;
In> f(x,y):=x*y;
Out> True;
In> f(3)+f(3,2);
Out> 15;
Функции могут возвращать значения любого типа, или даже возвращать значения разных типов в разные моменты времени.

Yacas предопределяет True и False как булевские значения. Функции, возвращающие булевские значения, называются предикатами. Например, IsNumber() и IsInteger() предикаты в стандартной библиотеке:
In> IsNumber(2+x);
Out> False;
In> IsInteger(15/5);
Out> True;
Когда присваиваются переменным, правые части сначала оцениваются, потом присваиваются. Таким образом,
a:=2*2
установит a в 4. Однако это не верно для функций. Когда вводите f(x):=x+x, правая часть, x+x, не оценивается перед присвоением. Это может быть форсировано вызывом Eval():
f(x):=Eval(x+x)
сначала оценит x+x в 2*x, прежде чем присваивать его функции пользователя f. Этот специфичный пример не очень полезен, но он понадобится, когда выполняемая справа операция дорогая. Например, если мы оцениваем разложение в ряд Тейлора перед присваиванием его определенной пользователем функции, ядру не понадобится создавать ряд Тейлора каждый раз, когда вызывается определенная пользователем функция.

Строки и списки.
В добавление к числам и переменным, Yacas поддерживает строки и списки. Строки - это просто последовательности символов, заключенные в двойные кавычки, например:
"это строка с \"кавычками\" внутри"
Списки есть упорядоченные группы объектов, как обычно. Yacas представляет списки помещением объектов между скобок и разделением их запятыми. Список, состоящий из a, b и c может быть введен, если напечатать {a, b, c}. В Yacas'е вектора представляются как списки, а матрицы как списки списков. На самом деле, каждое выражение Yacas'а может быть конвертировано в список. (смотри ниже).

Доступ к объектам в списке можно получать через оператор [ ]. Например: когда вы вводите
uu:={a,b,c,d,e,f};
тогда
uu[2];
оценивается в b и
uu[2 .. 4];
оценивается в {b, c, d}. Выражение "интервал"
2 .. 4
оценивается в {2,3,4}. Заметьте, что пробелы вокруг оператора .. необходимы, иначе парсер не сможет различить его от части числа.

Другое использование списков - это ассоциированный список, иногда называемый хэш-таблицей, который реализован в Yacas'е просто как список пар ключ-значение. Ключи должны быть строками, а значения могут быть любыми объектами. Ассоциированные списки могут также работать как мини-базы данных. В качестве примера сначала введите
u:={};
и затем
u["name"]:="Isaia";
u["occupation"]:="prophet";
u["is alive"]:=False;
Теперь u["name"] возвратит "Isaia". Список u теперь содержит три подсписка, как мы можем видеть:
In> u;
Out> { {"is alive", False}, {"occupation",
"prophet"}, {"name", "Isaia"} };
Списки оценивают свои аргументы и возвращают список с результатами оценивания каждого элемента. Так, печать {1+2,3}; будет оцениваться в {3,3}.

Идея использования списков чтобы представлять выражения восходит к языку LISP, разработанному в 1970х. Из небольшого набора операций над списками могут быть построены очень мощные алгоритмы. Списки также могут быть использованы как аргументы функций, когда ожидается переменное число аргуметнов.

Давайте сейчас попробуем некоторые списковые операции:
In> m:={a,b,c};
Out> True;

In> Length(m);
Out> 3;

In> Reverse(m);
Out> {c,b,a};

In> Concat(m,m);
Out> {a,b,c,a,b,c};

In> m[1]:="blah blah";
Out> True;
In> m;
Out> {"blah blah",b,c};

In> Nth(m,2);
Out> b;
В справочном руководстве описаные еще многие операции.

Линейная алгебра
Векторы фиксированных размерностей представляются как списки своих компонент. Список {1,2,3} будет трехмерным вектором с компонентами 1, 2 и 3. Матрицы представлены как вектор векторов.

Компонентам векторов могут присваиваться значения просто как элементам, поскольку они на самом деле элементы списка:
In> l:=ZeroVector(3);
Out> True;
In> l;
Out> {0,0,0};
In> l[ 2 ]:=2;
Out> True;
In> l;
Out> {0,2,0};
Yacas может выполнять умножение матриц, векторов и чисел как обычно в линейной алгебре:
In> v:={1,0,0,0}
Out> {1,0,0,0};
In> E4:={ {0,u1,0,0},{d0,0,u2,0},
{0,d1,0,0},{0,0,d2,0}}
Out> {{0,u1,0,0},{d0,0,u2,0},
{0,d1,0,0},{0,0,d2,0}};
In> CharacteristicEquation(E4,x)
Out> x^4-x*u2*d1*x-u1*d0*x^2;
In> Expand(%,x)
Out> x^4-(u2*d1+u1*d0)*x^2;
In> v+E4*v+E4*E4*v+E4*E4*E4*v
Out> {1+u1*d0,d0+(d0*u1+u2*d1)*d0,
d1*d0,d2*d1*d0};
Стандартная библиотека скриптов Yacas также включает взятие определителя и обратной матрици, нахождение собственных векторов и чисел (в простых случаях) и решение линейных систем уравнений, таких как Ax=b, где A - матрица, а x и b - векторы. Поддерживаются еще несколько матричных операций. Для деталями смотрите справочное руководство.

Управление выполнением: условия, циклы, блоки.
Язык Yacas включает несколько конструкций и функций для управления выполнением. Цикл можно органиpовать либо с функциональным вызовом ForEach(), либо c While(). Функция ForEach(x, list) body выполняет тело цикла body для каждого элемента списка и каждый раз присваивает этот элемент переменной x. Функциональный вызов While(predicate) body повторяет тело "body" до тех пор, пока предикат "predicate" не вернет False.

Условное выполнение реализовано функциональным вызовом If(predicate, body1, body2), который работает как конструкция (predicate) ? body1 : body2 в языке Си. Если условие истинно, оценивается "body1", иначе оценивается "body2", и возвращается соответствующее значение. Например, абсолютное значение числа может быть вычислено так:
absx := If( x>=0, x, -x );
(Библиотечная функция Abs() уже это делает).

Если необходимо выполнить последовательно несколько операций, чтобы получить результат, вы можете использовать функциональный вызов Prog() или эквивалентную конаструкцию [ ].

Чтобы проиллюстрировать эти особунности, давайте создадим список всех четных целых от 2 до 20 и вычислим произведение всех этих чисел кроме тех, которые делятся на 3. (То, что следует далее, не обязательно самый экономичный способ сделать это в Yacas'е.)
In> L := {};
Out> {};
In> i := 2;
Out> 2;
In> While(i<=20) [ L:= Append(L, i); \
i := i+2; ]
Out> True;
In> L;
Out> {2,4,6,8,10,12,14,16,18,20};
In> answer := 1;
Out> 1;
In> ForEach(i, L) If (Mod(i, 3)!=0, \
answer := answer * i);
Out> True;
In> answer;
Out> 2867200;
Мы использовали более короткую форму If(предисак, тело) только с одним телом, которое исполняется, когда предикат имеет место. Если условие не имеет место, этот функциональный вызов возвращает False.

Примеры.
Это небольшой тур по возможностям, предоставляемым Yacas'ом в настоящее время. Заметьте, что этот список примеров далеко неполон. Yacas содержит несколько сотен каманд, из которых только несколько показаны здесь.

Дополнительные примеры вычислений могут быть найдены здесь:
Прочие возможности.
100!
Вычислить большой факториал используя целые с произвольной точностью.
ToBase(16,255);
Обратить в другому основанию счисления
Expand((1+x)^5);
Расширить выражение в полином.
Apply("+",{2,3});
Приложить оператор к списку аргументов. Этот пример оценивается до 5.
Apply({{x,y},x+y},{2,3});
Применить чистую функцию к списку аргументов. Этот пример также оценивается до 5.
D(x)D(x) Sin(x);
Взять производные от функции.
Solve(a+x*y==z,x);
Решить уравнение относительно переменной.
Limit(x,0) Sin(x)/x;
Вычислить предел функции, когда переменая стремится к величине.
Newton(Sin(x),x,3,0.0001);
Использовать метод Ньютона для численного нахождения нуля функции.
DiagonalMatrix({a,b,c});
Создать диагональную матрицу с элементами, указанными в векторе, на диагонали.
Integrate(x,a,b) x*Sin(x);
Интегрировать функцию по переменной x от a до b.
Factors(x^2-1);
Разложить полином на множители
Apart(1/(x^2-1),x);
Создать разложение на простые дроби.

Более длинное вычисление с построением графика.
Теперь пример почти настоящего численного рассчета с использованием Yacas. Задача была в том, чтобы визуализировать конкретное точное решение эллиптического дифференциального уравнения. Решение было найдено как фесконечный ряд. Нам необходимо оценить этот бесконечный ряд численно и нарисовать его график для частных значений параметров.

Функция g(q,phi,chi) задана так:
g(q,phi,chi)=1/(2*Pi)*Sin(q*phi)/Sin(2*q*phi)
+1/Pi*Sum(n,0,Infinity,
Cos(n*chi)*Sin(Sqrt(q2-n2)*phi)
/Sin(2*Sqrt(q2-n2)*phi))
Здесь q, phi и chi - числовые переметры задачи. Мы хотели бы построить график этого ряда, оцененного в фиксированных точках q и phi как функции chi между 0 и 2Pi.

Чтобы решить эту задачу мы подготавливаем отдельный файл со следующим кодом для Yacas:
/* Вспомогательная функция */
g1(n, q, phi, chi) := [
Local(s);
s := q^2-n^2;
N(Cos(n*chi) * If(s=0,
1/2, /* Специальный случай s=0:
избегать деления на 0 */
Sin(Sqrt(s)*phi)/Sin(2*Sqrt(s)*phi)
/* теперь s != 0 */
/* заметьте, что Sqrt(s) может быть
здесь мнимым */
)
);
];
/* Главная функция */
g(q, phi, chi) := [
Local(M, n);
M := 16;
/* Exp(-M) будет точностью */
/* Использовать N() чтобы вызвать численное
оценивание */
N(1/2*Sin(q*phi)/Sin(2*q*phi)) +
/* Оценить необходимое число
слагаемых ряда */
Sum(n, 1, N(1+Sqrt(q^2+M^2/phi^2)),
g1(n, q, phi, chi)) ;
];
/* Параметры */
q:=3.5;
phi:=2;
/* Сделать функцию для построения графика:
она должна иметь только один аргумент */
f(x) := g(q, phi, x);
/* График от 0 до 2*Pi с 80 точками */
Plot2D(f(x), 0: 2*Pi);

Назовем этот файл "funl" и исполним этот скрипт, напечатав
Load("funl");
После этого вы должны увидеть окно с графиком.

Узнаем кое-что еще.
Опции командной строки.
Операцией по умолчанию для Yacas является работы в интерактивном консольном режиме. Yacas принимает несколько опций, которые изменяют эту операцию. Опции могут комбинироваться, например, yacas -pc имя_файла выполнит файл неинтерактивно. Вот сводка опций:
yacas -c
Подавить печать приглашений In> и Out>. Полезно для неинтерактивных сессий.
yacas -f
Читает стандартный ввод как один файл, но исполняет только первое выражение. (Вы можете использовать блок утверждений, чтобы выполнить несколько утверждений.)
yacas -p
Не использует возможности терминала, никакого затейливого редактирования в командной строке и никаких экранирующих последовательностей не печатается. Полезно для неинтерактивых сессий.
yacas -t
Включить некоторую дополнительную функциональность вызова истории в консольном режиме: после исполнения команды из списка истории, следующая неизмененная команда из из списка истории быдет автоматически вставлена в командную строку.
yacas [опции] имя_файла
Читает и исполняет команды в файле и завершает работу. Эквивалентно функции Load().
yacas -v
Печатает информацию о версии и завершает работу.
yacas -d
Печатает путь к библиотечному каталогу Yacas и завершает работу.
yacas -- patchload
Загрузит каждый файл из командной строки не обычной командой Load, а командой PatchLoad. Это полезно для генерации страниц html для интернета используя скриптовый язык Yacas, во многом аналогично языку php.
yacas --init [файл]
Говорит системе загрузить файл как файл инициализации. Иначе он загружает файл yacasinit.ys из каталока скриптов.
yacas --rootdir [каталог]/
Говорит системе, где найти библиотечные скрипты. Здесь [каталог]/ - путь, который передается в DefaultDirectory и имена файлов будут просто присоединены к нему, так что вы должны убедиться, что добавили наклонную черту или обратную (в зависимости от платформу) к концу. Возможно также дать список директорий, разделенных двоеточием, например yacas --rootdir scripts/:morescripts/ .
yacas --archive [файл]
Использовать сжатый архив вместо библиотеки скриптов.

Yacas имеет экспериментальную систему, в которой файлы могут быть сжаты в один файл и доступны через это опцию. Преимущества следующие:
  1. Меньшее использование диска/памяти (полезно, если Yacas используется на маленьких ручных компьютерах).
  2. Нет проблем с разделителями пути к каталогу: "путь/файл" будет всегда разрешен в правильный файл, неважно на какой платформе (читай: Windows).
  3. Время старта программы может немного улучшиться, поскольку меньший файл загружается с диска (если доступ к диску медленный) и затем разжимается в памяти, что может быть много быстрее, чем загрузка с диска.

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

Чтобы приготовить сжатый библиотечный архив, перейдите в каталог ramscripts/ и напечатайте
make -f makefile.compressor
Результатом должен быть файл архива scripts.dat . Затем запустите Yacas с опциемй командной строки --archive scripts.dat, имея втекущем каталоге файл scripts.dat .

Причина, по которой файл scripts.dat не строится автоматически, в том, что не проверено, работает ли этот процесс на всех платформах.

Когда архив присутствует, Yacas попробует загрузить его перед загрузкой скриптов из библиотечных каталогов. Печать
make archivetest -f makefile.compressor
в каталоге ramscripts/ запускает все тестовые скрипты используя заархивированные файлы.

В настоящее время поддерживаются следующие схемы сжатия: не сжатый и сжатый с помощью minilzo. Удаление пробелов и коментариев из файла скриптов может быть отключено путем редактирования файла compressor.cpp (переменная strip_script).

Использование в режиме клиент/сервер.
В дополнение к консольному режиму скрипт yacas_client предоставляет специальную возможность экспериментальной постоянной сессии. Средствами этого скрипта пользователь может настраивать сторонние приложения, чтобы передавать команды постоянно работающему "серверу Yacas" и получать вывод. "Сервер Yacas" автоматически запускается программой yacas_client. Он может работать на удаленном компьютере; в этом случае пользователь должен иметь счет на удаленном компьютере и привелегии выполнять там yacas_client, равно как и доступ через rsh или ssh. Цель yacas_client в том, чтобы позволить пользователям передавать команды Yacas'у в ходе постоянной сессии, пока работа идет в другом приложении, таком как текстовый редактор.

Скрипт yacas_client читает команды Yacas со стандартного ввода и передает их работающему "серверу Yacas"; затем ждет 2 секунды и печатает весь тот вывод, которые произвел Yacas к этому времени. Использование может выглядеть так:
8:20pm Unix>echo "x:=3" | yacas_client
Starting server.
[editvi] [gnuplot]
True;
To exit Yacas, enter  Exit(); or quit
  or Ctrl-c. Type ?? for help.
Or type ?function for help on a function.
Type 'restart' to restart Yacas.
To see example commands, keep typing
  Example();
In> x:=3
Out> 3;
In> 8:21pm Unix>echo "x:=3+x" | yacas_client
In> x:=3+x
Out> 6;
In> 8:23pm Unix>yacas_client -stop
In> quit
Quitting...
Server stopped.
8:23pm Unix>
Постоянство сессии означает, что Yacas запомнил значение x между запусками yacas_client. Если двух секунд Yacas'у не достаточно, чтобы произвести результат, вывод будет показан в следующий раз, когда вы вызовите yacas_client.

"Сервер Yacas" запускается автоматически когда используется впервые и может быть остановлен либо по выходе из Yacas'а, либо явным использованием опции yacas_client -stop, в этом случае yacas_client не читает стандартный ввод.

Скрипт yacas_client читает стандартный ввод и пишет на стандартный вывод, так что он может быть использован через выполнение удаленной оболочки. Например, если счет "user" на удаленном компьютере "remote.host" доступен через ssh, тогда yacas_client может быть использован удаленно, например так:
echo "x:=2;" | \
  ssh user@remote.host yacas_client
На данном компьютере, исполняющем "сервер Yacas", каждый пользователь может иметь только одну постоянную сессию Yacas.

Составные выражения.
Несколько выражений могут быть сгруппированы вместе использованием скобок [ и ]. Составное выражение [a; b; c;]; оценивает a, затем b, затем c и возвращает результат оценивания c.

Переменная может быть объявлена локальной по отношению к блоку составного выражения функцией Local(перменная1, переменная2, ...).

Дистрибутивность относительно списков ("threading").
Некоторые функции в Yacas могут быть сделаны дистрибутивными относительно списков. Это означает, что вызов функции со списком в качестве аргумента даст результатом список результатов вызова функции дляф каждого элемента спика. Например,
Sin({a,b,c});
вернет {Sin(a),Sin(b),Sin(c)}.  Такой образ действий реализован для большинства нормальных аналитических функций и арифметических операторов.

Функции как списки.
Внутри себя Yacas пердставляет все атомарные выражения (числа и переменные) как строки и все составные выражения как списки, точно как  LISP. Попробуйте FullForm(a+b*c); и увидете, как на экране появится текст (+ a (* b c)). Также любое выражение может быть обращено в список функцией Listify() или обратно в выражение функцией UnList().
In> Listify(a+b*(c+d));
Out> {+,a,b*(c+d)};
In> UnList({Atom("+"),x,1});
Out> x+1;
Заметьте, что первый элемент списка - это имя функции +, которое эквивалентно представимо как Atom("+"), и что подвыражение b*(c+d) не было обращено в списковую форму.

Чистые функции являются эквивалентами "лямбда-выражений" LISP'а, другими словами, это выражения Yacas'а, представляющие тела функций. В настоящее время они реализованы с использованием списков и оператора Apply(). Следующая строка
Apply( {{x,y},x+y} , {2,3} );
оценится до 5. Здесь {{x,y}, x+y} есть список, с которым оператор Apply() обращается как с чистой функцией, символы x и y становятся локальными переменными, связанными с передаваемыми параметрами, а {x+y} становится телом функции.

Еще о синтаксисе.
Синтаксис обрабатывается инфиксным грамматическим анализатором. Приоритет операций может быть явно указан скобками, если нормальный приоритет не тот, какой вам нужен.  Большую часть времени вы будете вводить выражения вида Func(var1.var2) или, используя инфиксные операторы, a*(b+c)), префиксные операторы: -x, или постфиксные операторы: x++. Анализатор чувствителен к регистру и, в общем, правила синтаксиса напоминают язык Си. Последние по прядку, но не по значимости, так называемые функции "с телом", которые, против обычных функций, таких как f(x,y), держат последний аргумент вне списка аргументов, заключенного в скобки: f(x) y. Это похоже чем-то на математический "оператор" f(x), действующий на y. Типичный пример функций "с телом"  - функция While, которая выглядит как "While(predicate) body;". Оператор производной D(x) также определен как функция "с телом". Заметьте, что если бы мы определили функцию "с телом" A только с одним арументом, мы должны были бы использовать ее так: "A() x;" и это выглядело бы странно. В этом случае мы бы сделали "A" префиксным оператором и затем синтаксис сталбы несколько яснее: "A x;".

Однако, вне зависимости от представления, внутренне все функции и операторы одинаковы и просто принимают определенное число аргументов. Пользователь может определить или переопределить любые операторы как с "нормальными" именами типа "A", или с именами, составленными из одного или более специальных символов + - * / = ` ~ : ! @ # $ ^ & _ | < > и объявлять их инфиксными, постфиксными или префиксными операторами, также как и нормальными функциями и функциями "с телами". (Символ % сохранен для результата предыдущего выражения; десятичная точка - особая и не может быть частью имени оператора, только если все имя состоит целиком из точек, то есть оператор "...".) Некоторые из этих операторов и комбинаций уже определены в библиотеке скриптов Yacas'а, например операторы "синтаксической сласти" типа := или <--, но они могут быть в принципе переопределены или стерты. Эти "специальные" операторы ни в коей мере не специфичны для системы, за исключением их синтаксиса.

Всем инфиксным, префиксным или постфиксным операторам и функциям "с телом" назначены приоритеты (целые числа); инфиксные операторы дополнительно имеют левый и правый приоритет. Все они оказывают влияние только на синтаксис ввода и могут быть обустроены для удобства пользователя. Например, инфиксный оператор "*" имеет приоритет меньший, чем приоритет инфиксного оператора "+", и поэтему выражение типа a+b*c интерпретируется обычным образом, то есть a+(b*c).

Важнная оговорка: вы должны убедиться, что всегда печатаете пробул между любыми символами, которые могли бы составить оператор. Например, после того, как вы определили новую функцию "@@(x):=x^2;" выражения, такие как "a:=@@(b)@", напечатанные без пробелов, станут причиной ошибки, если только вы не определите также оператор ":=@@". Это из-за того, что синтаксический анализатор не остоновится у ":=", когда попытается придать смысл этому выражению. Правильный способ справиться с этим в том, чтобы вставить пробел: "a:= @@(b)". Аналогично, a!+3 вызовет ошибку, если вы не определите "!+", так что используйте пробел: a! +3. Пробелы не требуются в ситуациях, таких как "a:=-1", но это только потому, что оператор :=- на самом деле определен в Yacas.

Давайте теперь рассмотрим пример на эти синтаксические особенности. Предположим, мы хотели определить функцию F(x,y)=x/y+y/x. Мы могли бы использовать стандартный синтаксис:
In> F(a,b) := a/b + b/a;
Out> True;
и затем использовать функцию как F(1,2); Мы могли бы также объявить эквивалентныю инфиксную операцию, назовем ее "xx", так что мы могли бы писать просто 1 xx 2. Инфиксные операции должны иметь приоритет, так что давайте назначим ей приоритет обычного оператора деления. Объявление будет следующим:
In> Infix("xx", OpPrecedence("/"));
Out> True;
In> a xx b := a/b + b/a;
Out> True;
In> 3 xx 2 + 1;
Out> 19/6;
Проверьте математику и обратите внимение на то, как работает приоритет!

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

Наконец, мы могли решить быть полностью гибкими с этой важной функцией и определить ее также как математический оператор ##. Сначала мы определяем ## как функция "с телом" и затем продолжаем как раньше:
In> Bodied("##", OpPrecedence("/"));
Out> True;
In> ##(a) b := a xx b;
Out> True;
In> ##(1) 3 + 2;
Out> 16/3;
Мы использовали имя ##, но мы могли также использовать любое другое имя, такое как xx или F или даже _-+@+-_.  Кроме возможности запутать самого себя это не имеет значения, как вы называете функции, которые определяете.

В настоящее время есть в Yacas одно ограничение: когда имя функции объявлено инфиксным (префиксным, постфиксным) или "с телом", далее оно всегда будет интерпретироваться таким образом. Если мы объявляем f "с телом", мы млжем позднее определить различные функции с именем f с различным числом аргументов, однако все эти функции должны быть "с телом".

Написание правил упрощений.
Математические вычисления требуют разносторонних преобразований с символьными величинами. Вместо попытки определить все возможные преобразования, Yacas предоставляет простую в использовании схему шаблонных совпадений для манипулирования выражениями в соответствии с определенными пользователем правилами. Yacas сам спроектирован как небольшое ядро, исполняющее большую библиотеку правил, чтобы искать совпадения и заменять  шаблоны. Примеры могут быть найдены в библиотечных файлах "standard", "stdfuncs", "deriv" и "solve", которые входят в комплект Yacas.

Одно простое применение правил совпадений с шаблоном в том, чтобы определять новые функции. (На самом деле, это - единственный путь для Yacas узнавать о новых функциях). В качестве примера давайте определим функцию fЮ которая будет оценивать факториалы неотрицательных целых чисел. Сначала мы определим предикат, чтобы проверять, действительно ли аргумент - неотрицательное целое, и мы используем этот предикат и очевидную рекурсию f(n)=n*f(n-1), чтобы оценить факториал. Все это достигается следующими тремя строчками:
10 # f(0) <-- 1;
20 # f(n_IsIntegerGreaterThanZero)
  <-- n*f(n-1);
IsIntegerGreaterThanZero(_n) <--
  IsInteger(n) And n>0;
Мы сперва определили два "правила упрощения" для новой функции f().Затем мы поняли, что нам надо определить предикат IsIntegerGreaterThanZero(). Предикат IsIntegerGreaterThanZero() на самом деле уже определен в стандартной библиотеке и называется IsPositiveInteger, так что не было необходимо, говоря прямо, определять наш собственный предикат, чтобы делать то же; мы сделали это для иллюстрации.

Первые две строки рекурсивно определяют факториальную функцию f(n)=n*(n-1)*...*1. Правилам даны значения приоритетов 10 и 20, так что первое правило будет применяться первым.  Между прочим, факторал тоже определен в стандартной библиотеке как постфиксный оператор "!" и связан с внутренней процедурой, которая гораздо быстрее, чем рекурсия в нашем примере.

Оператор <-- определяет правило, которое надо применить к конкретной функции. (Операция <-- не может быть применена к атому). Символ _n в правиле для IsIntegerGreaterThanZero() указывает, чтолюбой объект, который окажется аргументом предиката, совпадает с шаблоном и присваивается локальной переменной n. Выражение справа от <-- может использовать n (без знака подчеркивания) как переменную.

Тепрь рассмотрим правила для функции f. Первое правило просто указывает, что f(0) в любом выражении должно быть заменено на 1. Второе правило несколько ближе к теме. n_IsIntegerGreaterThanZero есть совпадение для аргумента f с оговоркой, что предикат IsIntegerGreaterThanZero(n) должен вернуть True (истина), в противнос случае совпадения не произойдет. Оператор знак подчеркивания должен использоваться только в левой части оператора правила <--.

Есть другое, несколько более длинное, но эквивалентное написание второго правила:
20 # f(_n)_(IsIntegerGreaterThanZero(n))
  <-- n*f(n-1);
Знак подчеркивания после функционального объекта обозначает "постпредикат", который должен возвратить истину, иначе совпадения не происходит. Этот предикат может быть сложным выражением, включающим несколько логических операций, в противовес простой проаверке только одного предиката в конструкции (n_IsIntegerGreaterThanZero). Постпредикат также может использоватьт переменную n (без знака подчеркивания). Эквивалентные способы определения того же предиката в шаблонах правил существую просто для удобства.

Значения приоритетов для правил даны числом, за которым следует оператор #. Это число определяет упорядочивание приоритетов для правил поиска по шаблону, с  наимельшим разрешенным нулевым значением приоритета, то есть правила с приоритетом 0 будут испробоваться первыми. Один и тот же приоритет могут иметь несколько правил: это просто означает, что неважно, в каком порядке эти правила пробуются. Если никакого числа не указано, приоритет полагается нулевой. В нашем примере правило  f(0) <-- 1 должно беть применено раньше рекурсивного правила, иначе рекурсия никогда не окончится. Но поскольку нет других правил, качающихся f , назначение чисел 10 и 20 произвольно, и они также могли бы быть 500 и 501.