[327e91]: ru / maxima-tarnavsky-6.xml  Maximize  Restore  History

Download this file

262 lines (166 with data), 39.0 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../main.xsl"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" id="maxima-tarnavsky-6" xml:lang="ru">
<head>
<title>Тихон Тарнавский. Maxima. Работа c файлами и фактами</title>
<link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"/>
<link rel="schema.DCTERMS" href="http://purl.org/dc/terms/"/>
<meta name="DC.identifier" scheme="DCTERMS.URI" content="http://maxima.sourceforge.net/ru/maxima-tarnavsky-6.html"/>
</head>
<body>
<p>Впервые было опубликовано в «<a href="http://www.linuxformat.ru/">Linux Format</a>» <a href="http://www.linuxformat.ru/download/85.pdf">№12&#x00a0;(86), декабрь&#x00a0;2006&#x00a0;г</a>.</p>
<p>В прошлый раз мы остановились на возможностях программирования, предназначенных для написания собственных функций и модулей к Maxima&#x00a0;— и теперь для их полноценного использования рассмотрим несколько инструментов работы с файлами, позволяющих сохранять и загружать эти функции и модули на диск и с диска. Далее речь пойдет о наложении определенных условий на неизвестные и значения функций. Напоследок познакомимся с функциями по работе… с функциями: это один из очень мощных инструментов, позаимствованных из функционального программирования; а также разберем несколько более крупных учебных примеров, использующих многое из изученного нами во всех статьях цикла.</p>
<h3>Учимся читать и писать</h3>
<p>Среди средств для операций с файлами функции с наиболее очевидными именами&#x00a0;— save и load&#x00a0;— имеют, вопреки привычной для Maxima логичности всех названий, различный контекст. Первая предназначена для выгрузки Maxima-выражений в виде исходных кодов на Lisp, так что если вы не знаток Lisp (да и реализации внутренних механизмов Maxima), то эта функция представляет лишь чисто академический интерес. Посему подробнее мы займемся другими функциями&#x00a0;— для обработки так называемых пакетных (batch) файлов, хранящих выражения уже в синтаксисе самой Maxima. А поскольку в виде таких файлов поставляется немалое количество функционала Maxima, то начнем с загрузки. И вот о второй из очевидно-именуемых функций здесь уже будет рассказано.</p>
<p>Функции чтения файлов с выражениями Maxima существует три: <kbd>demo(<var>имя-файла</var>)</kbd>, <kbd>batch(<var>имя-файла</var>)</kbd> и <kbd>batchload(<var>имя-файла</var>)</kbd>. Первая предназначена для загрузки так называемых демо-файлов, задуманных, как и явствует из названия, для демонстрационных примеров. Она загружает демо-файл и выполняет его в пошаговом режиме, ожидая нажатия <kbd>Enter</kbd> после выполнения каждой строки. В составе Maxima поставляется значительное количество демо-файлов; упоминания о них можно найти в документации, а сами файлы несложно обнаружить среди содержимого пакета <kbd>maxima-share</kbd> (либо, в случае отсутствия такового в вашем дистрибутиве, просто <kbd>maxima</kbd>) по их расширению&#x00a0;<kbd>.dem</kbd>.</p>
<p>Функция <kbd>batch()</kbd> загружает Maxima-файл с расширением <kbd>.mac</kbd> или <kbd>.mc</kbd> (от первоначального названия программы&#x00a0;— Macsyma) и выполняет содержащиеся в нем выражения так, как если бы они вводились прямо в текущей сессии, то есть с отображением результата каждого выражения и назначением меток <kbd>%iN</kbd>, <kbd>%oN</kbd>. Функция <kbd>batchload()</kbd>, напротив, подгружает пакетный файл «молча»: все назначенные в нем функции и переменные становятся доступны, но результаты не видны, и весь хранимый ввод-вывод, включая значения символов <kbd>%</kbd> и <kbd>_</kbd> и результаты, возвращаемые функцией <kbd>%th()</kbd>, остается тем же, что и до вызова.</p>
<p>Функции <kbd>batch()</kbd> и <kbd>batchload()</kbd> используют при поиске файлов для загрузки путь (точнее сказать, шаблон, потому как в нем содержатся не только имена каталогов, но и допустимые расширения файлов), который хранится в переменной <kbd>file_search_maxima</kbd>. По умолчанию эта переменная содержит все каталоги, в которые устанавливаются <kbd>.mac</kbd>-файлы из пакетов Maxima, а также <kbd>~/.maxima</kbd>, предназначенный для пользовательских файлов. Для других функций загрузки существуют отдельные переменные: <kbd>file_search_lisp</kbd> и <kbd>file_search_demo</kbd>, смысл которых понятен из их названий.</p>
<p>Ну и под конец здесь нужно вспомнить о вышеназванной функции load. Она, фактически, является оберткой над двумя функциями: уже описанной выше <kbd>batchload()</kbd> и <kbd>loadfile()</kbd>, вторая, совершенно аналогично первой, загружает файл, но уже не с выражениями Maxima, а с исходным кодом Lisp, то есть является парной к функции <kbd>save()</kbd>. Функцию <kbd>load()</kbd> можно, в принципе, использовать вместо <kbd>batchload()</kbd>: путь <kbd>file_search_maxima</kbd> задан в ней раньше, чем <kbd>file_search_lisp</kbd>, так что в случае неоднозначности она будет загружать файлы Maxima; а кроме того, так короче.</p>
<p>Некоторый функционал Maxima содержится в неподгружаемых автоматически внешних файлах, которые, соответственно, нужно принудительно загрузить перед использованием:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-01-thumb.png" alt="" /></p>
<p>Помимо ручной загрузки нужного файла, можно также настроить Maxima на автоматическую подгрузку в случае вызова заданной функции. Делается это так: <kbd>setup_autoload(<var>имя-файла</var>,&#x00a0;<var>имена-функций</var>)</kbd>; нужные функции здесь перечисляются через запятую прямо после имени файла. Удобнее, конечно, будет не вызывать функцию <kbd>setup_autoload()</kbd> вручную (так в ней и толку немного), а настроить Maxima на автоматический ее запуск при старте программы. Файл, который, при его наличии, вызывается при каждом запуске Maxima, называется <kbd>maxima-init.mac</kbd> и самое логичное для него местоположение&#x00a0;— все тот же каталог <kbd>~/.maxima</kbd>. Конечно, он может содержать не только вызовы функции <kbd>setup_autoload()</kbd>, а любые выражения Maxima, которые вы хотите выполнять при каждом ее запуске. Использование этой функции может сделать вашу работу с Maxima намного более удобной в том случае, если вы часто используете некоторые из внешних функций Maxima или функции, вами же и написанные.</p>
<p>Для полноценного чтения файлов всего сказанного уже вполне достаточно, теперь перейдем к записи в них. Тут нас в первую очередь интересует функция <kbd>stringout()</kbd>, которая позволяет выгружать в файл любые выражения и функции Maxima в точно таком виде, в каком их загружают функции <kbd>demo()</kbd>, <kbd>batch()</kbd> и <kbd>batchload()</kbd>. С ее помощью можно писать выражения, которые вы хотите иметь во внешнем модуле, находясь непосредственно в интерфейсе Maxima, с последующей записью в этот самый модуль. Для выгрузки функций в один из стандартных каталогов Maxima (самым логичным вариантом будет, пожалуй, упомянутый выше <kbd>~/.maxima</kbd>) имя файла во всех вариантах вызова функции <kbd>stringout()</kbd> нужно задавать с полным путем; в случае задания имени без пути файл будет создан в текущем каталоге, то есть в том, откуда производился запуск Maxima.</p>
<p>Здесь, чтобы было интереснее и не приходилось писать в файлы всякую ерунду, немного прервемся и создадим пару небольших функций.</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-02-thumb.png" alt="" /></p>
<p>Эта функция возвращает список всех простых чисел, меньших чем заданное целое число. Сначала мы проверяем, является ли аргумент целым числом и делаем это простейшим образом: в случае невыполнения условия оператор <kbd>if</kbd>, напомню, вернет <kbd>false</kbd>. Генерируется список тоже самым простым и коротким в реализации способом&#x00a0;— рекурсией. (примечание для людей, далеких от программирования: рекурсивная функция&#x00a0;— это функция, вызывающая саму себя; чаще всего такие функции строятся по принципу индукции). Здесь используется функция Maxima по имени <kbd>prev_prime()</kbd>, которая возвращает простое число, предшествующее заданному целому.</p>
<p>У рекурсии, при всей ее простоте реализации, есть неоспоримый минус&#x00a0;— только один, но весьма существенный: чрезвычайная требовательность к объему памяти. Поэтому, для обеспечения возможности получать последовательности из больших простых чисел, добавим в наш учебный пример еще одну функцию:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-03-thumb.png" alt="" /></p>
<p>Смысл, думаю, понятен по аналогии с предыдущей: теперь мы еще и ограничили возвращаемый список снизу.</p>
<p>Теперь, когда у нас уже есть <kbd>primesbetween()</kbd>, первую функцию можно написать по «принципу чайника»&#x00a0;— сведя задачу к предыдущей:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-04.png" alt="" /></p>
<p>Теперь вернемся к <kbd>stringout()</kbd>. Эта функция, как и многие другие, может принимать несколько различных вариантов аргументов, первым из которых всегда выступает имя файла для записи, а остальные отвечают за то, что же именно будет туда записано. В варианте <kbd>stringout(<var>имя-файла</var>,&#x00a0;[<var>начало</var>,&#x00a0;<var>конец</var>])</kbd> записаны будут ячейки ввода с номерами от «начала» до «конца» включительно:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-05.png" alt="" /></p>
<pre>$ cat .maxima/primes.mac
primes(n):=if integerp(n) then (if n &lt;= 2 then [] else
append(primes(prev_prime(n)),[prev_prime(n)]));
primesbetween(n,m):=if integerp(n) and integerp(m) then
(if m &lt;= 2 or prev_prime(m) &lt;= n then [] else
append(primesbetween(n,prev_prime(m)),[prev_prime(m)]));</pre>
<p>Как видите, по умолчанию вывод получается не слишком красивым, поэтому сразу рассмотрим один ключ, влияющий на его формат. Долго рассказывать о нем смысла нет, лучше показать на примере:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-06-thumb.png" alt="" /></p>
<pre>$ cat .maxima/primes.mac
primes(x):=if integerp(x)
then (if x &lt;= 2 then []
else append(primes(prev_prime(x)),[prev_prime(x)]));
primesbetween(n,m):=if integerp(n) and integerp(m)
then (if m &lt;= 2 or prev_prime(m) &lt;= n then []
else append(primesbetween(n,prev_prime(m)),
[prev_prime(m)]));</pre>
<p>Представления о правилах отступов у создателей этой опции несколько специфичные, но тем не менее, результат стал намного читабельнее. Так что, если вы планируете сохранять выражения Maxima не только для того, чтобы потом загружать их обратно, а желаете редактировать созданные файлы, я рекомендую вам прописать <kbd>grind:true</kbd> глобально в файле <kbd>~/.maxima/maxima-init.mac</kbd>.</p>
<p>Идем дальше. С помощью ключевого слова <kbd>input</kbd> можно выгрузить в файл все ячейки ввода разом:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-07-thumb.png" alt="" /></p>
<pre>$ cat primes-sample.mac
primes(n):=if integerp(n)
then (if n &lt;= 2 then []
else append(primes(prev_prime(n)),[prev_prime(n)]));
primesbetween(n,m):=if integerp(n) and integerp(m)
then (if m &lt;= 2 or prev_prime(m) &lt;= n then []
else append(primesbetween(n,prev_prime(m)),
[prev_prime(m)]));
primes1(n):=primesbetween(1,n);
stringout(".maxima/primes.mac",[1,2]);
grind:true;
stringout(".maxima/primes.mac",[1,2]);
(N:[random(100000)],for i thru 9 do N:append(N,[N[i]+random(100000)]),N);
(P:[],for i thru 10 do P:append(P,primesbetween(N[i]-50,N[i])),P);</pre>
<p>Кроме <kbd>input</kbd>, есть еще два ключевых слова: <kbd>functions</kbd> и <kbd>values</kbd>. Первое позволяет записать определения всех функций, второе&#x00a0;— присвоение всем символам выражений их текущих значений:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-08-thumb.png" alt="" /></p>
<pre>$ cat .maxima/primes.mac
primes(n):=if integerp(n)
then (if n &lt;= 2 then []
else append(primes(prev_prime(n)),[prev_prime(n)]));
primesbetween(n,m):=if integerp(n) and integerp(m)
then (if m &lt;= 2 or prev_prime(m) &lt;= n then []
else append(primesbetween(n,prev_prime(m)),
[prev_prime(m)]));
primes1(n):=primesbetween(1,n);
$ cat primes-sample.mac
primes(n):=if integerp(n)
then (if n &lt;= 2 then []
else append(primes(prev_prime(n)),[prev_prime(n)]));
primesbetween(n,m):=if integerp(n) and integerp(m)
then (if m &lt;= 2 or prev_prime(m) &lt;= n then []
else append(primesbetween(n,prev_prime(m)),
[prev_prime(m)]));
primes1(n):=primesbetween(1,n);
N:[49900,61971,153219,244360,290427,347723,396481,465378,522906,568462];
P:[49853,49871,49877,49891,61927,61933,61949,61961,61967,153191,244313,244333,
244339,244351,244357,290383,290393,290399,290419,347707,347717,396437,
396443,396449,396479,465331,465337,465373,522857,522871,522881,522883,
522887,568433,568439,568441,568453];</pre>
<p>И кроме всего этого, функцию <kbd>stringout()</kbd> можно вызвать с непосредственным перечислением в аргументах конкретных выражений. В этом случае, надо заметить, будут сохраняться не ячейки, содержащие заданные выражения, а именно сами выражения. То есть, если перечислить символ, для которого задано значение, то в файл будет записано только это значение. С именами функций, заданными непосредственно, дело обстоит не лучше: функцию таким образом задать, по сути, вообще нельзя: если просто написать ее имя, то вместо функции будет подставлен одноименный символ (или его значение, если оно задано). Но из обеих ситуаций есть выход. Для функций&#x00a0;— штатный: функция <kbd>fundef</kbd>, которая принимает имя любой пользовательской функции и возвращает ее определение в точности в таком же виде, в каком оно было введено (или могло бы быть введено) в «командной строке» Maxima, с точностью до пробелов:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-09-thumb.png" alt="" /></p>
<pre>$ cat .maxima/primesbetween.mac
primesbetween(n,m):=if integerp(n) and integerp(m)
then (if m &lt;= 2 or prev_prime(m) &lt;= n then []
else append(primesbetween(n,prev_prime(m)),
[prev_prime(m)]));
$ cat .maxima/primes1.mac
primes(n):=if integerp(n)
then (if n &lt;= 2 then []
else append(primes(prev_prime(n)),[prev_prime(n)]));
primes1(n):=primesbetween(1,n);</pre>
<p>А для символов можно использовать небольшую хитрость: блокировать вычисление переданного выражения, а в нем написать сначала сам символ, а потом через двоеточие&#x00a0;— его же, предварив знаком принудительного вычисления (два апострофа):</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-10-thumb.png" alt="" /></p>
<pre>$ cat random-primes.mac
P:[49853,49871,49877,49891,61927,61933,61949,61961,61967,153191,244313,244333,
244339,244351,244357,290383,290393,290399,290419,347707,347717,396437,
396443,396449,396479,465331,465337,465373,522857,522871,522881,522883,
522887,568433,568439,568441,568453];</pre>
<p>В довершение темы работы с файлами стоит обратить внимание еще на один момент: при загрузке файлы в текущем каталоге не ищутся&#x00a0;— и как раз для него надо задавать путь, причем полный, а не через <kbd>./<var>имя-файла</var></kbd>:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-11-thumb.png" alt="" /></p>
<h3>«Прослушайте объявление»</h3>
<p>Теперь поговорим о функциях, позволяющих налагать определенные условия на выражения, которыми оперирует Maxima. Таких функций существует две, и достаточно разноплановых; но определенная связь между ними есть, так как все условия, заданные ими на данный момент, хранятся в общей «базе». Первая из этих функций называется <kbd>declare</kbd> (<em>объявлять</em>). С ее помощью можно объявлять весьма разнообразные факты о произвольных символах или выражениях; синтаксис ее весьма прост: <kbd>declare(<var>имя</var>,&#x00a0;<var>факт</var>)</kbd> или <kbd>declare(<var>имя<sub>1</sub></var>,&#x00a0;<var>факт<sub>1</sub></var>,&#x00a0;<var>имя<sub>2</sub></var>,&#x00a0;<var>факт<sub>2</sub></var>,&#x00a0;…)</kbd>; факты задаются с помощью ключевых слов. Сами факты я бы разделил на три группы: «технические» факты Maxima, позволяющие использовать наделенный ими символ некоторым специальным образом при вводе выражений; факты о символах (атомарных выражениях); и факты о значениях функций. К первым относятся, к примеру, свойства <kbd>evflag</kbd> и <kbd>evfun</kbd>, о которых шла речь в описании функции <kbd>ev</kbd>; некоторые штатные функции обладают ими по умолчанию, а с помощью функции <kbd>declare</kbd> мы можем присвоить эти свойства любым другим, в том числе и пользовательским, функциям. Вторая группа фактов несет информацию о неизвестных; например, мы можем указать, что некоторая неизвестная является константой, или что ее значение&#x00a0;— целое. И третья группа&#x00a0;— примерно то же самое, но о функциях; примеры: четная функция (<var>f</var>(−<var>x</var>)&#x00a0;=&#x00a0;<var>f</var>(<var>x</var>)), аддитивная (<var>f</var>(<var>x</var>&#x00a0;+&#x00a0;<var>y</var>)&#x00a0;=&#x00a0;<var>f</var>(<var>x</var>)&#x00a0;+&#x00a0;<var>f</var>(<var>y</var>)) или целочисленная. Для краткости просто перечислим наиболее интересные из возможных фактов, сгруппировав соответственно трем упомянутым группам.</p>
<h4>Технические факты</h4>
<h5 style="padding-top: 0; margin-top: 0"><kbd>evfun</kbd></h5>
<p>Позволяет применять функцию или переменную как опцию, то есть «<kbd><var>выражение</var>,&#x00a0;<var>имя-функции</var></kbd>» вместо «<kbd><var>имя-функции</var>(<var>выражение</var>)</kbd>» или «<kbd><var>выражение</var>,&#x00a0;<var>имя-переменной</var></kbd>» вместо «<kbd><var>имя-переменной</var>:true;&#x00a0;<var>выражение</var></kbd>». Подробнее см.&#x00a0;«<a href="maxima-tarnavsky-2.html">Maxima. Функции и операторы</a>».</p>
<h5><kbd>bindtest</kbd></h5>
<p>Запрещает использовать символ в выражениях до присвоения ему значения. При таком использовании Maxima выдаст ошибку. Пример см.&#x00a0;в&#x00a0;документации.</p>
<h5><kbd>feature</kbd></h5>
<p>Делает заданное имя именем свойства (факта), что дает возможность использовать его точно так же, как все перечисленные здесь имена.</p>
<h4>Факты о символах</h4>
<h5 style="padding-top: 0; margin-top: 0"><kbd>constant</kbd></h5>
<p>Имя трактуется как константа.</p>
<h5><kbd>scalar</kbd></h5>
<p>Имя трактуется как скалярная величина. На это также влияет флаг <kbd>assumescalar</kbd>: если он равен <kbd>true</kbd>, то все неопределенные символы воспринимаются как скаляры. Тут есть небольшая коллизия: если верить документации, то по умолчанию <kbd>assumescalar</kbd> равен <kbd>false</kbd>, реально же в Maxima&#x00a0;5.10.0 он равен <kbd>true</kbd>.</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-12.png" alt="" /></p>
<h5><kbd>nonscalar</kbd></h5>
<p>Имя трактуется как не-скалярная величина, то есть матрица или вектор.</p>
<h5><kbd>integer</kbd>, <kbd>noninteger</kbd></h5>
<p>Целое и нецелое число.</p>
<h5><kbd>even</kbd>, <kbd>odd</kbd></h5>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-13.png" alt="" /></p>
<p>Четное и нечетное целое число.</p>
<h4>Факты о функциях</h4>
<h5 style="padding-top: 0; margin-top: 0"><kbd>rassociative</kbd></h5>
<p>Объявляет функцию как «ассоциативную» по правому аргументу.</p>
<h5><kbd>lassociative</kbd></h5>
<p>Аналогично&#x00a0;— по левому аргументу.</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-14.png" alt="" /></p>
<h5><kbd>nary</kbd></h5>
<p>Объявляет «<var>n</var>-арную» функцию. Это и два предыдущих названия не совсем точны: <var>n</var>-арной правильно называть функцию от <var>n</var> аргументов, а лево- и правоассоциативной&#x00a0;— функции именно с односторонней ассоциативностью, то есть, для «лево-» <var>f</var>(<var>f</var>(<var>a</var>,<var>b</var>),<var>c</var>)&#x00a0;&#x00a0;<var>f</var>(<var>a</var>,<var>b</var>,<var>c</var>)&#x00a0;&#x00a0;<var>f</var>(<var>a</var>,<var>f</var>(<var>b</var>,<var>c</var>)). А в Maxima все три факта объявляют на самом деле полно-ассоциативную функцию от произвольного числа аргументов, а различаются только тем, как будут расставлены скобки по умолчанию.</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-15.png" alt="" /></p>
<h5><kbd>symmetric</kbd>/<kbd>commutative</kbd></h5>
<p>Оба ключевых слова объявляют функцию как симметричную (коммутативную).</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-16.png" alt="" /></p>
<h5><kbd>antisymmetric</kbd></h5>
<p>Объявляет функцию как антисимметричную.</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-17.png" alt="" /></p>
<h5><kbd>outative</kbd></h5>
<p>Константа выносится за знак функции.</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-18.png" alt="" /></p>
<p>Многие из фактов, которые можно устанавливать с помощью функции <kbd>declare</kbd>, сохраняются в «базе данных» фактов. Узнать текущее состояние этой базы можно с помощью функции <kbd>facts()</kbd>. Ее можно вызывать, либо передав в качестве единственного аргумента имя, список фактов по которому мы хотим получить, либо вообще без аргументов&#x00a0;— тогда будут выданы все известные факты обо всех пользовательских именах. Удалить свойства позволяет функция <kbd>remove()</kbd>. Она, как и многие другие, имеет несколько вариантов вызова. Будучи вызвана как <kbd>remove(<var>имя</var>,&#x00a0;<var>свойство</var>)</kbd> или <kbd>remove(<var>имя<sub>1</sub></var>,&#x00a0;<var>свойство<sub>1</sub></var>,&#x00a0;<var>имя<sub>2</sub></var>,&#x00a0;<var>свойство<sub>2</sub></var>,&#x00a0;…)</kbd>, она лишает каждое переданное имя одного соответствующего ему свойства. Можно также передавать ей списки имен и свойств: <kbd>remove([<var>имя<sub>1</sub></var>,&#x00a0;<var>имя<sub>2</sub></var>,&#x00a0;…], [<var>свойство<sub>1</sub></var>,&#x00a0;<var>свойство<sub>2</sub></var>,&#x00a0;…])</kbd>; тогда каждое имя из списка будет лишено всех перечисленных свойств. Пар списков тоже может быть более одной: <kbd>remove(<var>список-имен<sub>1</sub></var>,&#x00a0;<var>список-свойств<sub>1</sub></var>,&#x00a0;<var>список-имен<sub>2</sub></var>,&#x00a0;<var>список-свойств<sub>2</sub></var>,&#x00a0;…)</kbd>&#x00a0;— этот вызов аналогичен последовательным <kbd>remove(<var>список-имен<sub>1</sub></var>,&#x00a0;<var>список-свойств<sub>1</sub></var>); remove(<var>список-имен<sub>2</sub></var>,&#x00a0;<var>список-свойств<sub>2</sub></var>); …</kbd> И последний интересующий нас вариант&#x00a0;<kbd>remove(all,&#x00a0;<var>свойство</var>)</kbd> удаляет «свойство» со всех имен, у которых оно есть.</p>
<p>Вторая «условная» функция&#x00a0;— это функция <kbd>assume()</kbd> (допускать, принимать). Здесь все проще: в качестве аргументов ей можно передавать в любом количестве самые обыкновенные равенства и неравенства. Напомню только, что задавать их нужно не в синтаксической, а в логической форме, то есть не «<kbd>a=b</kbd>», «<kbd>a#b</kbd>», а «<kbd>equal(a,b)</kbd>», «<kbd>not&#x00a0;equal(a,b)</kbd>». Из логических операторов допускается также использование <kbd>and</kbd> (по сути <kbd>assume(x>0&#x00a0;and&#x00a0;x&lt;1)</kbd> это то же самое, что и <kbd>assume(x>0,&#x00a0;x&lt;1)</kbd>), но не <kbd>or</kbd>&#x00a0;— база фактов не поддерживает информацию вида «или»; и речь не о синтаксисе, а именно о конструкциях, то есть выражения типа <kbd>not(a>b&#x00a0;and&#x00a0;a&lt;c)</kbd> тоже недопустимы. Факты, добавленные <kbd>assume()</kbd>, также видны функции <kbd>facts()</kbd>:</p>
<p>Ключевое слово <kbd>kind</kbd> используется только для отображения тех фактов из базы, которые добавлены с помощью <kbd>declare()</kbd>.</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-19.png" alt="" /></p>
<p>Если факты, заданные функцией <kbd>declare()</kbd>, удаляются вызовом <kbd>remove()</kbd>, то для <kbd>assume()</kbd> есть своя «обратная» функция&#x00a0;<kbd>forget()</kbd>, которая также принимает любое количество условий (точно таких же как и <kbd>assume()</kbd>), либо в качестве отдельных аргументов, либо списком.</p>
<p>Общая база фактов используется этими двумя не очень похожими функциями неспроста: все, кому эти факты могут пригодиться, используют обе их разновидности, причем одновременно. Например, уже известный нам предикат <kbd>is</kbd>:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-20.png" alt="" /></p>
<p>Еще один пример использования <kbd>assume()</kbd>/<kbd>declare()</kbd>&#x00a0;— возможность избежать неопределенностей. Вы, возможно, помните, как в одном из примеров статьи «<a href="maxima-tarnavsky-4.html">Maxima. Алгебра и начала анализа</a>» в ответ на попытку посчитать некий интеграл Maxima задала нам вопрос о знаке входящего в него символа. Вот в таких ситуациях тоже может пригодиться <kbd>assume</kbd>, дабы предвосхитить расспросы:</p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-21.png" alt="" /></p>
<p class="labeled-image"><img src="i/tarnavsky/6/maxima6-22.png" alt="" /></p>
<p>Вот мы и подошли к концу «теоретической» части. Надеюсь, функционала, рассмотренного на протяжении шести статей, будет достаточно для решения многих задач, а также для того, чтобы черпать дальнейшие сведения из документации&#x00a0;— ведь мы уже изучили такие вещи, благодаря которым Maxima становится не просто «вычислялкой» отдельных небольших примеров, а настоящей «средой программирования с математическим уклоном», позволяющей создавать свои собственные математические «типы данных»&#x00a0;— числовые системы, функционалы и прочая и прочая&#x00a0;— и полноценные программные модули, которые могут использовать весь встроенный (или также собственноручно достроенный) функционал Maxima. Рассмотрим, напоследок, более серьезный учебный пример, в котором эти возможности можно будет лучше прочувствовать. Одна заявленная тема у нас пока осталась нераскрытой&#x00a0;— функции для работы с функциями и «глубокой» обработки выражений. Но это настолько серьезный инструмент, что на маленьких примерах его рассматривать было бы бессмысленно, а потому мы поговорим о нем в приложении-практикуме. Удачи!</p>
<p>&#x00a0;<a href="maxima-tarnavsky-6-diff.html">Пишем свой diff()</a></p>
</body>
</html>