@c Language=Russian
@c Encoding=UTF-8
@c File=Program.texi 
@c OriginalRevision=1.33
@c TranslatedBy: (c) 2007-06 Vadim V. Zhytnikov <vvzhyt@gmail.com>

@menu
* Введение в программирование на Maxima::  
* Функции и переменные для программирования на Maxima::  
@end menu

@node Введение в программирование на Maxima, Функции и переменные для программирования на Maxima, Программы Maxima, Программы Maxima
@section Введение в программирование на Maxima

Maxima предоставляет цикл @code{do}, для итерации, а также более простые управляющие
конструкции, такие как @code{go}.

@c end concepts Program Flow
@node Функции и переменные для программирования на Maxima,  ,  Введение в программирование на Maxima, Программы Maxima
@section Функции и переменные для программирования на Maxima

@deffn {Функция} backtrace ()
@deffnx {Функция} backtrace (@var{n})
Печатает стек вызовов, т.е. список функций, которые
вызывают текущую активную функцию.

Вызов @code{backtrace()} печатает весь стек вызовов.

Вызов @code{backtrace (@var{n})} печатает @var{n} последних
функций, включая текущую.

@c IS THIS STATMENT REALLY NEEDED ?? 
@c (WHY WOULD ANYONE BELIEVE backtrace CANNOT BE CALLED OUTSIDE A DEBUGGING CONTEXT??)
Функция @code{backtrace} может вызываться внутри программы, функции или из интерактивного
приглашение а не только из контекста отладки.

Примеры:

@itemize @bullet
@item
Вызов @code{backtrace()} печатает весь стек вызовов.

@example
(%i1) h(x) := g(x/7)$
(%i2) g(x) := f(x-11)$
(%i3) f(x) := e(x^2)$
(%i4) e(x) := (backtrace(), 2*x + 13)$
(%i5) h(10);
#0: e(x=4489/49)
#1: f(x=-67/7)
#2: g(x=10/7)
#3: h(x=10)
                              9615
(%o5)                         ----
                               49
@end example
@end itemize

@itemize @bullet
@item
Вызов @code{backtrace (@var{n})} печатает @var{n} последних
функций, включая текущую.

@example
(%i1) h(x) := (backtrace(1), g(x/7))$
(%i2) g(x) := (backtrace(1), f(x-11))$
(%i3) f(x) := (backtrace(1), e(x^2))$
(%i4) e(x) := (backtrace(1), 2*x + 13)$
(%i5) h(10);
#0: h(x=10)
#0: g(x=10/7)
#0: f(x=-67/7)
#0: e(x=4489/49)
                              9615
(%o5)                         ----
                               49
@end example
@end itemize

@opencatbox{Категории:}
@category{Отладка}
@closecatbox
@end deffn

@deffn {Специальный оператор} do
Команда @code{do} используется для выполнения итераций.  Т.к. команда @code{do} 
довольно обща, то она будет описана в двух частях.
Сначала будет описана обычная форма, аналогична используемым 
в нескольких других языках программирования (Fortran, Algol, PL/I и т.д.).
Далее будут описаны другие возможности.

Существует три формы команды, отличающиеся только условием
завершения.  А именно:

@itemize @bullet
@item
@code{for @var{variable}: @var{initial_value} step @var{increment}
      thru @var{limit} do @var{body}}
@item
@code{for @var{variable}: @var{initial_value} step @var{increment}
      while @var{condition} do @var{body}}
@item
@code{for @var{variable}: @var{initial_value} step @var{increment}
      unless @var{condition} do @var{body}}
@end itemize

@c UGH. DO WE REALLY NEED TO MENTION THIS??
(Как альтернатива, команда @code{step} может быть задана после условия завершения или 
границы.)

Параметры @var{initial_value}, @var{increment}, @var{limit} и @var{body} 
могут быть произвольными выражениями.  Если приращение равно 1, то команда
"@code{step 1}" может быть опущена.

Перед выполнение команды @code{do}, переменной @var{variable} 
(далее называемой управляющей переменной) присваивается начальное значение @var{initial_value}. 
Далее: (1) если значение управляющей переменной превышает верхнюю границу,
задаваемую @code{thru}, или если условие @code{unless} равно @code{true}, 
или условие @code{while} равно @code{false}, то @code{do} завершается.
(2) Вычисляется @var{body}.  (3) Приращение добавляется к управляющей переменой.  
Шаги (1) -- (3) повторяются до выполнения условия завершения.  
Можно задавать несколько условий завершения.  В этом случае команда @code{do} 
завершается при удовлетворении любого из них.

Обычно тест @code{thru} выполняется, когда управляющая переменная становится больше 
@var{limit}, когда приращение @var{increment} неотрицательно, или, если переменная становится меньше
@var{limit}, когда приращение @var{increment} отрицательно.
Параметры @var{increment} и @var{limit} могут быть нечисловыми выражениями, если 
может быть определено их неравенство.  Однако, если @var{increment} не является
синтаксически отрицательным (например, отрицательным числом) в момент начала выполнения команды @code{do},
то Maxima предполагает положительность @var{increment} во время выполнения @code{do}.
Если на самом деле @var{increment} окажется неположительным, то @code{do} 
может не завершиться надлежащим образом.

Отметим, что @var{limit}, @var{increment} и условие завершения вычисляются
заново для каждой итерации.  Таким образом, если какое-либо из этих выражений требует 
длительного вычисления, но не меняется от итерации к итерации при вычислении
@var{body}, то будет более эффективным присвоить их
значение переменной до выполнения @code{do} и использовать эту переменную.

Значение, обычно возвращаемое командой @code{do}, есть атом @code{done}.
Однако, функция @code{return} может быть использована внутри @var{body}
для завершения @code{do} и задания возвращаемого значения.
Отметим, что вызов @code{return} внутри цикла @code{do}, расположенного  в блоке
@code{block}, завершает @code{do} а не блок @code{block}.  
Функция @code{go} не может быть использована для выхода из команды @code{do} 
в объемлющий блок @code{block}.

Управляющая переменная всегда локальна в рамках @code{do}, т.е.
эту переменную можно использовать внутри цикла и это не
окажет влияния на переменную с тем же именем вне @code{do}.  
После завершения @code{do}, управляющая переменная остается несвязанной.

@example
(%i1) for a:-3 thru 26 step 7 do display(a)$
                             a = - 3

                              a = 4

                             a = 11

                             a = 18

                             a = 25
@end example

@example
(%i1) s: 0$
(%i2) for i: 1 while i <= 10 do s: s+i;
(%o2)                         done
(%i3) s;
(%o3)                          55
@end example

Отметим, что условие @code{while i <= 10} эквивалентно условию
@code{unless i > 10} или условию @code{thru 10}.

@example
(%i1) series: 1$
(%i2) term: exp (sin (x))$
(%i3) for p: 1 unless p > 7 do
          (term: diff (term, x)/p, 
           series: series + subst (x=0, term)*x^p)$
(%i4) series;
                  7    6     5    4    2
                 x    x     x    x    x
(%o4)            -- - --- - -- - -- + -- + x + 1
                 90   240   15   8    2
@end example

что дает 8 членов ряда Тейлора для @code{e^sin(x)}.

@example
(%i1) poly: 0$
(%i2) for i: 1 thru 5 do
          for j: i step -1 thru 1 do
              poly: poly + i*x^j$
(%i3) poly;
                  5      4       3       2
(%o3)          5 x  + 9 x  + 12 x  + 14 x  + 15 x
(%i4) guess: -3.0$
(%i5) for i: 1 thru 10 do
          (guess: subst (guess, x, 0.5*(x + 10/x)),
           if abs (guess^2 - 10) < 0.00005 then return (guess));
(%o5)                  - 3.162280701754386
@end example

В этом примере вычисляется отрицательный квадратный корень числа 10 
с использованием метода Ньютона-Рафсона с максимальным числом итераций 10.  
Если условие сходимости не будет выполнено, то возвращается значение
@code{done}.

Вместо добавления значения к управляющей переменной возможен
другой способ ее изменения на каждом цикле итерации.
В этом случае можно использовать @code{next @var{expression}} вместо @code{step @var{increment}}.
Что вызывает присваивание управляющей переменной значения @var{expression}
при каждой итерации.

@example
(%i6) for count: 2 next 3*count thru 20 do display (count)$
                            count = 2

                            count = 6

                           count = 18
@end example

@c UGH. DO WE REALLY NEED TO MENTION THIS??
Синтаксис @code{for @var{variable} from @var{value} ...do...} может
быть использован как альтернатива @code{for @var{variable}: @var{value} ...do...}.  
Что позволяет располагать @code{from @var{value}} после значений @code{step} или @code{next}, 
или после условия завершения.  Если @code{from @var{value}} опущено, то в качестве начального
значения предполагается 1.

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

@example
(%i1) x: 1000$
(%i2) thru 20 do x: 0.5*(x + 5.0/x)$
(%i3) x;
(%o3)                   2.23606797749979
(%i4) sqrt(5), numer;
(%o4)                   2.23606797749979
@end example

Если необходимо, то можно полностью опустить условия завершения
и использовать только @code{do @var{body}}, что вызывает бесконечное 
вычисление @var{body}.  В этом случае для завершения @code{do} следует
использовать @code{return}.

@example
(%i1) newton (f, x):= ([y, df, dfx], df: diff (f ('x), 'x),
          do (y: ev(df), x: x - f(x)/y, 
              if abs (f (x)) < 5e-6 then return (x)))$
(%i2) sqr (x) := x^2 - 5.0$
(%i3) newton (sqr, 1000);
(%o3)                   2.236068027062195
@end example

@c DUNNO IF WE NEED THIS LEVEL OF DETAIL; THIS ARTICLE IS GETTING PRETTY LONG
(Заметим, что @code{return} вызывает возвращение текущего значения @code{x} 
в качестве значения @code{do}.  Блок @code{block} завершается, и значение @code{do} 
возвращается в качестве значения блока, т.к. @code{do} является последней командой @code{block}.)

В Maxima доступна еще одна форма команды @code{do}.  Ее синтаксис:

@example
for @var{variable} in @var{list} @var{end_tests} do @var{body}
@end example

Элементы списка @var{list} (произвольные выражения) 
последовательно присваиваются переменной @var{variable}
Для каждой итерации вычисления @var{body}.  
Необязательное условие @var{end_tests} может использоваться для завершения @code{do},
иначе цикл завершится при исчерпании @var{list} или при выполнении @code{return} 
внутри @var{body}.  (На самом деле, @var{list} может быть любым
неатомарным выражением, последовательные части которого будут использованы в итерации.)

@example
(%i1)  for f in [log, rho, atan] do ldisp(f(1))$
(%t1)                                  0
(%t2)                                rho(1)
                                     %pi
(%t3)                                 ---
                                      4
(%i4) ev(%t3,numer);
(%o4)                             0.78539816
@end example

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@deffn {Функция} errcatch (@var{expr_1}, ..., @var{expr_n})
Вычисляет одно за другим выражения  @var{expr_1}, ..., @var{expr_n} 
и, если не возникает ошибок, возвращает @code{[@var{expr_n}]} (список).  
Если при вычислении одного из аргументов возникает ошибка, то
@code{errcatch} предотвращает дальнейшее распространение ошибки и возвращает
пустой список @code{[]} без дальнейшего вычисления аргументов.

Функция @code{errcatch} полезна в пакетных @code{batch} файлах,
если ожидается возможное возникновение ошибки, что без 
перехвата ошибки приводит к завершению @code{batch}.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@deffn {Функция} error (@var{expr_1}, ..., @var{expr_n})
@deffnx {Системная переменная} error
Вычисляет и печатает @var{expr_1}, ..., @var{expr_n}, и
затем вызывает ошибку, что возвращает управление на верхний
уровень Maxima или ближайшему объемлющему @code{errcatch}.

Значением переменной @code{error} является список, описывающий ошибку.
Первый элемент @code{error} -- строка форматирования,
которая объединяет все строки из параметров @var{expr_1}, ..., @var{expr_n},
остальные элементы -- есть значения нестроковых аргументов.

Функция @code{errormsg()} форматирует и печатает @code{error},
что повторяет печать самой последней ошибки.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@deffn {Функция} errormsg ()
Повторяет печать самой последней ошибки.
Информация об ошибке содержится в переменной @code{error}, и @code{errormsg} 
форматирует и печатает ее.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@c REPHRASE
@c AT LEAST SHOULD LIST VARIANTS HERE
@deffn {Специальный оператор} for
Используется в циклах. См. @code{do} для описания средств Maxima для
организации итераций.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@deffn {Функция} go (@var{tag})
Используется внутри блока @code{block} для передачи управления на команду,
помеченную параметром @code{go}.  Для пометки команды в блоке, перед ней помещают
другую команду в виде атома (метки).  Например:

@example
block ([x], x:1, loop, x+1, ..., go(loop), ...)
@end example

Аргументом @code{go} должна быть метка, определенная в том же блоке @code{block}.  
Нельзя использовать @code{go} для передачи управления на метку в блоке отличном от
того, где находится сама команда @code{go}.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@c NEEDS CLARIFICATION, EXPANSION, EXAMPLES
@c THIS ITEM IS IMPORTANT
@deffn {Специальный оператор} if
Условное вычисление. Существуют различные формы условного выражения @code{if}.

@code{if @var{cond_1} then @var{expr_1} else @var{expr_0}}
вычисляет и возвращает значение @var{expr_1}, если значение @var{cond_1} равно @code{true},
иначе вычисляет и возвращает значение @code{expr_0}.

@code{if @var{cond_1} then @var{expr_1} elseif @var{cond_2} then @var{expr_2} elseif ... else @var{expr_0}}
вычисляет и возвращает значение @var{expr_k}, если @var{cond_k} равно @code{true} 
а все предыдущие условия равны @code{false}.
Если не одно из условий не равно @code{true}, то вычисляется и возвращается значение @var{expr_0}.

Если завершающее @code{else} отсутствует, то в конце подразумевается @code{else false}.
Т.е. @code{if @var{cond_1} then @var{expr_1}} эквивалентно 
@code{if @var{cond_1} then @var{expr_1} else false},
а @code{if @var{cond_1} then @var{expr_1} elseif ... elseif @var{cond_n} then @var{expr_n}}
эквивалентно
@code{if @var{cond_1} then @var{expr_1} elseif ... elseif @var{cond_n} then @var{expr_n} else false}.

Альтернативы @var{expr_0}, ..., @var{expr_n} могут быть произвольными выражениями Maxima,
включая вложенные @code{if} выражения.
Альтернативы не упрощаются и не вычисляются до тех пор пока соответствующее условие
не равно @code{true}.

Условия @var{cond_1}, ..., @var{cond_n} являются выражениями, которые потенциально или фактически
вычисляются в @code{true} или @code{false}.
Если значение условия не равно ни @code{true}, ни @code{false}, то
поведение @code{if} управляется глобальной переменной @code{prederror}.
Если @code{prederror} равна @code{true}, то любое значение условия, отличное от @code{true} или @code{false},
считается ошибкой.  Иначе, условия, которые не вычисляются в @code{true} или @code{false},
считаются допустимыми и результатом вычисления является условное выражение.

Вместе с другими элементами, условия могут включать следующие логические операторы
и операторы отношения.

@c - SEEMS LIKE THIS TABLE WANTS TO BE IN A DISCUSSION OF PREDICATE FUNCTIONS; PRESENT LOCATION IS OK I GUESS
@c - REFORMAT THIS TABLE USING TEXINFO MARKUP (MAYBE)
@example
Операция                    Символ      Тип
 
меньше                      <           отношение, инфиксный
меньше или равно            <=          отношение, инфиксный
равенство (синтаксическое)  =           отношение, инфиксный
отрицание =                 #           отношение, инфиксный
равенство (значение)        equal       отношение, функция
отрицане equal              notequal    отношение, функция
больше или равно            >=          отношение, инфиксный
больше                      >           отношение, инфиксный
и                           and         логический, инфиксный
или                         or          логический, инфиксный
нет                         not         логический, префиксный
@end example

@opencatbox{Категории:}
@category{Программирование}
@category{Функции предикаты}
@closecatbox
@end deffn

@c NEEDS CLARIFICATION
@c THIS ITEM IS IMPORTANT
@anchor{map}
@deffn {Функция} map (@var{f}, @var{expr_1}, ..., @var{expr_n})
Возвращает выражение, с оператором верхнего уровня таким же как у выражений 
@var{expr_1}, ..., @var{expr_n} но аргументы которого являются результатами 
применения @var{f} к соответствующим аргументам выражений @var{expr_i}.  
Здесь, @var{f} -- либо функция @math{n} аргументов, либо @code{lambda} 
выражение с @math{n} аргументами.

Если переменная @code{maperror} равна @code{false}, то @code{map}:
(1) остановится на самом коротком выражении @var{expr_i}, если не все @var{expr_i} 
имеют одинаковую длину; (2) применит @var{f} к [@var{expr_1}, @var{expr_2},...],
если не все @var{expr_i} имеют одинаковый тип.  Если @code{maperror} равна @code{true},
то в оба вышеуказанных случая считаются ошибкой.

Одним из применений функции @code{map} является применение какой-либо функции
(например, @code{partfrac}) к каждому члену большого выражения вместо применения 
ее к выражению целиком, что может привести к слишком сложным вычислениям и,
как следствие, к нехватке памяти.

@c IN THESE EXAMPLES, SPELL OUT WHAT IS THE MAIN OPERATOR 
@c AND SHOW HOW THE RESULT FOLLOWS FROM THE DESCRIPTION STATED IN THE FIRST PARAGRAPH
@example
(%i1) map(f,x+a*y+b*z);
(%o1)                        f(b z) + f(a y) + f(x)
(%i2) map(lambda([u],partfrac(u,x)),x+1/(x^3+4*x^2+5*x+2));
                           1       1        1
(%o2)                     ----- - ----- + -------- + x
                         x + 2   x + 1          2
                                         (x + 1)
(%i3) map(ratsimp, x/(x^2+x)+(y^2+y)/y);
                                      1
(%o3)                            y + ----- + 1
                                    x + 1
(%i4) map("=",[a,b],[-0.5,3]);
(%o4)                          [a = - 0.5, b = 3]


@end example

@opencatbox{Категории:}
@category{Применение функций}
@closecatbox
@end deffn

@deffn {Функция} mapatom (@var{expr})
Возвращает @code{true} тогда и только тогда, когда выражение @var{expr} рассматривается map-процедурами
как атомарное выражение.  К таким "mapatoms" относятся атомы, числа (включая рациональные) и
переменные с индексом.
@c WHAT ARE "THE MAPPING ROUTINES", AND WHY DO THEY HAVE A SPECIALIZED NOTION OF ATOMS ??

@opencatbox{Категории:}
@category{Функции предикаты}
@closecatbox
@end deffn

@c NEEDS CLARIFICATION
@defvr {Управляющая переменная} maperror
Значение по умолчанию: @code{true}

Если переменная @code{maperror} равна @code{false}, то все @code{map} функции, например:

@example
map (@var{f}, @var{expr_1}, @var{expr_2}, ...)
@end example

(1) остановится на самом коротком выражении @var{expr_i}, если не все @var{expr_i} 
имеют одинаковую длину; (2) применит @var{f} к [@var{expr_1}, @var{expr_2},...],
если не все @var{expr_i} имеют одинаковый тип.  

Если @code{maperror} равна @code{true},
то в оба вышеуказанных случая считаются ошибками.

@opencatbox{Категории:}
@category{Применение функций}
@closecatbox
@end defvr


@defvr {Управляющая переменная} mapprint
Значение по умолчанию: @code{true}

Если переменная @code{mapprint} равна @code{true}, то функции 
@code{map}, @code{mapl} и @code{fullmap} в определенных ситуациях 
выдают различные сообщения.  Включая ситуации, когда @code{map} использует
@code{apply} или @code{map} обрезает до самого короткого списка.

Если @code{mapprint} равна @code{false}, то данные сообщения подавляются.

@opencatbox{Категории:}
@category{Применение функций}
@closecatbox
@end defvr

@c NEEDS CLARIFICATION
@deffn {Функция} maplist (@var{f}, @var{expr_1}, ..., @var{expr_n})
Возвращает список с @var{f}, примененной к частям выражений 
@var{expr_1}, ..., @var{expr_n}.
@var{f} -- есть имя функции или лямбда-выражение.

Функция @code{maplist} отличается от @code{map (@var{f}, @var{expr_1}, ..., @var{expr_n})},
которая возвращает выражение с главным оператором, одним для всех выражений @var{expr_i}
(за исключеним упрощений и случая, когда @code{map} выполняет @code{apply}).

@opencatbox{Категории:}
@category{Применение функций}
@closecatbox
@end deffn

@c NEEDS CLARIFICATION
@defvr {Управляющая переменная} prederror
Значение по умолчанию: @code{false}

Если @code{prederror} равна @code{true}, то ошибка случается всякий раз, как только
вычисленное значение предиката команды @code{if} или функции @code{is} отлично от 
@code{true} или @code{false}.

Если  @code{false}, то в этом случае возвращается @code{unknown}.  
Работа с @code{prederror: false} не поддерживается транслированном коде.
Однако, @code{maybe} поддерживается в транслированном коде.

См. также @code{is} и @code{maybe}.

@opencatbox{Категории:}
@category{Программирование}
@category{Функции предикаты}
@closecatbox
@end defvr

@deffn {Функция} return (@var{value})
Используется для явного выхода из блока, делая @var{value} значением этого блока.
См. @code{block} для более детального описания.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@c NEEDS CLARIFICATION
@deffn {Функция} scanmap (@var{f}, @var{expr})
@deffnx {Функция} scanmap (@var{f}, @var{expr}, bottomup)
Применяет функцию @var{f} к @var{expr} рекурсивно начиная с верхнего уровня и глубже.
Это может быть полезно, если, например, требуется полная факторизация:

@example
(%i1) exp:(a^2+2*a+1)*y + x^2$
(%i2) scanmap(factor,exp);
                                    2      2
(%o2)                         (a + 1)  y + x
@end example

Отметим, что то, как @code{scanmap} применяет данную функцию @code{factor} к подвыражениям
@var{expr}, зависит от формы этого выражения.  И если дана другая форма @var{expr},
то результат @code{scanmap} может быть другим.  Так, @code{%o2} не получится,
если @code{scanmap} применить к раскрытому варианту @code{exp}:

@example
(%i3) scanmap(factor,expand(exp));
                           2                  2
(%o3)                      a  y + 2 a y + y + x
@end example

Еще один пример рекурсивного применения @code{scanmap} ко всем подвыражениям,
включая экспоненты:

@example
(%i4) expr : u*v^(a*x+b) + c$
(%i5) scanmap('f, expr);
                    f(f(f(a) f(x)) + f(b))
(%o5) f(f(f(u) f(f(v)                      )) + f(c))
@end example

@code{scanmap (@var{f}, @var{expr}, bottomup)} применяет @var{f} к @var{expr} с
самого глубокого уровня вверх.  Например, для неопределенной функции @code{f},

@example
scanmap(f,a*x+b) ->
   f(a*x+b) -> f(f(a*x)+f(b)) -> f(f(f(a)*f(x))+f(b))
scanmap(f,a*x+b,bottomup) -> f(a)*f(x)+f(b)
    -> f(f(a)*f(x))+f(b) ->
     f(f(f(a)*f(x))+f(b))
@end example

В данном случае результат одинаков для обоих вариантов вызова.

@opencatbox{Категории:}
@category{Применение функций}
@closecatbox
@end deffn

@deffn {Функция} throw (@var{expr})
Вычисляет выражение @var{expr} и "бросает" его значение вверх до ближайшего @code{catch}.  
Функции @code{throw} и @code{catch} реализуют механизм нелокального возврата.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@c NEED MORE HERE !!
@c AT LEAST SHOULD LIST ACCEPTABLE VARIANTS
@deffn {Специальный оператор} while
@deffnx {Специальный оператор} unless
См. @code{do}.

@opencatbox{Категории:}
@category{Программирование}
@closecatbox
@end deffn

@deffn {Функция} outermap (@var{f}, @var{a_1}, ..., @var{a_n})
Применяет функцию @var{f} к каждому элементу внешнего произведения 
@var{a_1} x @var{a_2} ... x @var{a_n}.

@var{f} -- есть имя функции с @math{n} аргументами или
лямбда-выражение с @math{n} аргументами.
Каждый элемент @var{a_k} может быть списком, списком списков, матрицей или любым другим выражением.

Значение @code{outermap} является вложенной структурой.
Пусть @var{x} является возвращаемым значением.
Тогда @var{x} имеет ту же структуру, как первый аргумент (список, список списков или матрица),
@code{@var{x}[i_1]...[i_m]} имеет ту же структуру, как второй аргумент (список, список списков или матрица),
@code{@var{x}[i_1]...[i_m][j_1]...[j_n]} имеет ту же структуру, как третий аргумент (список, список списков или матрица),
и т.д., где @var{m}, @var{n}, ... -- есть число индексов, необходимых для для доступа к элементам
каждого из аргументов (один для списка, два для матрицы, еще один для вложенного списка).
Аргументы, не являющиеся списками и матрицами, не оказывают влияния на возвращаемое значение.

Отметим, что результат @code{outermap} отличается от применения @var{f} ко всем
элементам внешнего произведения, возвращаемого @code{cartesian_product}.
Функция @code{outermap} сохраняет структуру аргументов в возвращаемом значении, а @code{cartesian_product} нет.

Функция @code{outermap} вычисляет свои аргументы.

См. также @code{map}, @code{maplist} и @code{apply}.
@c CROSS REF OTHER FUNCTIONS HERE ??

Примеры:

Простые примеры @code{outermap}.
Для прояснения комбинации аргументов, @code{F} оставлена неопределенной.

@c ===beg===
@c outermap (F, [a, b, c], [1, 2, 3]);
@c outermap (F, matrix ([a, b], [c, d]), matrix ([1, 2], [3, 4]));
@c outermap (F, [a, b], x, matrix ([1, 2], [3, 4]));
@c outermap (F, [a, b], matrix ([1, 2]), matrix ([x], [y]));
@c outermap ("+", [a, b, c], [1, 2, 3]);
@c ===end===
@example
(%i1) outermap (F, [a, b, c], [1, 2, 3]);
(%o1) [[F(a, 1), F(a, 2), F(a, 3)], [F(b, 1), F(b, 2), F(b, 3)], 
                                     [F(c, 1), F(c, 2), F(c, 3)]]
(%i2) outermap (F, matrix ([a, b], [c, d]), matrix ([1, 2], [3, 4]));
         [ [ F(a, 1)  F(a, 2) ]  [ F(b, 1)  F(b, 2) ] ]
         [ [                  ]  [                  ] ]
         [ [ F(a, 3)  F(a, 4) ]  [ F(b, 3)  F(b, 4) ] ]
(%o2)    [                                            ]
         [ [ F(c, 1)  F(c, 2) ]  [ F(d, 1)  F(d, 2) ] ]
         [ [                  ]  [                  ] ]
         [ [ F(c, 3)  F(c, 4) ]  [ F(d, 3)  F(d, 4) ] ]
(%i3) outermap (F, [a, b], x, matrix ([1, 2], [3, 4]));
       [ F(a, x, 1)  F(a, x, 2) ]  [ F(b, x, 1)  F(b, x, 2) ]
(%o3) [[                        ], [                        ]]
       [ F(a, x, 3)  F(a, x, 4) ]  [ F(b, x, 3)  F(b, x, 4) ]
(%i4) outermap (F, [a, b], matrix ([1, 2]), matrix ([x], [y]));
       [ [ F(a, 1, x) ]  [ F(a, 2, x) ] ]
(%o4) [[ [            ]  [            ] ], 
       [ [ F(a, 1, y) ]  [ F(a, 2, y) ] ]
                              [ [ F(b, 1, x) ]  [ F(b, 2, x) ] ]
                              [ [            ]  [            ] ]]
                              [ [ F(b, 1, y) ]  [ F(b, 2, y) ] ]
(%i5) outermap ("+", [a, b, c], [1, 2, 3]);
(%o5) [[a + 1, a + 2, a + 3], [b + 1, b + 2, b + 3], 
                                           [c + 1, c + 2, c + 3]]
@end example

Более детальное исследование работы @code{outermap}.
Первый, второй и третий аргументы являются матрицей, списком и матрицей соответственно.
Возвращаемое значение является матрицей.
Каждый элемент этой матрицы есть список, и каждый элемент данного списка -- матрица.

@c ===beg===
@c arg_1 :  matrix ([a, b], [c, d]);
@c arg_2 : [11, 22];
@c arg_3 : matrix ([xx, yy]);
@c xx_0 : outermap (lambda ([x, y, z], x / y + z), arg_1, 
@c                                                    arg_2, arg_3);
@c xx_1 : xx_0 [1][1];
@c xx_2 : xx_0 [1][1] [1];
@c xx_3 : xx_0 [1][1] [1] [1][1];
@c [op (arg_1), op (arg_2), op (arg_3)];
@c [op (xx_0), op (xx_1), op (xx_2)];
@c ===end===
@example
(%i1) arg_1 :  matrix ([a, b], [c, d]);
                            [ a  b ]
(%o1)                       [      ]
                            [ c  d ]
(%i2) arg_2 : [11, 22];
(%o2)                       [11, 22]
(%i3) arg_3 : matrix ([xx, yy]);
(%o3)                      [ xx  yy ]
(%i4) xx_0 : outermap (lambda ([x, y, z], x / y + z), arg_1, 
                                                     arg_2, arg_3);
               [  [      a        a  ]  [      a        a  ]  ]
               [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
               [  [      11       11 ]  [      22       22 ]  ]
(%o4)  Col 1 = [                                              ]
               [  [      c        c  ]  [      c        c  ]  ]
               [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
               [  [      11       11 ]  [      22       22 ]  ]
                 [  [      b        b  ]  [      b        b  ]  ]
                 [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
                 [  [      11       11 ]  [      22       22 ]  ]
         Col 2 = [                                              ]
                 [  [      d        d  ]  [      d        d  ]  ]
                 [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
                 [  [      11       11 ]  [      22       22 ]  ]
(%i5) xx_1 : xx_0 [1][1];
           [      a        a  ]  [      a        a  ]
(%o5)     [[ xx + --  yy + -- ], [ xx + --  yy + -- ]]
           [      11       11 ]  [      22       22 ]
(%i6) xx_2 : xx_0 [1][1] [1];
                      [      a        a  ]
(%o6)                 [ xx + --  yy + -- ]
                      [      11       11 ]
(%i7) xx_3 : xx_0 [1][1] [1] [1][1];
                                  a
(%o7)                        xx + --
                                  11
(%i8) [op (arg_1), op (arg_2), op (arg_3)];
(%o8)                  [matrix, [, matrix]
(%i9) [op (xx_0), op (xx_1), op (xx_2)];
(%o9)                  [matrix, [, matrix]
@end example

Функция @code{outermap} сохраняет структуру аргументов в возвращаемом значении, а @code{cartesian_product} нет.

@c ===beg===
@c outermap (F, [a, b, c], [1, 2, 3]);
@c setify (flatten (%));
@c map (lambda ([L], apply (F, L)), 
@c                      cartesian_product ({a, b, c}, {1, 2, 3}));
@c is (equal (%, %th (2)));
@c ===end===
@example
(%i1) outermap (F, [a, b, c], [1, 2, 3]);
(%o1) [[F(a, 1), F(a, 2), F(a, 3)], [F(b, 1), F(b, 2), F(b, 3)], 
                                     [F(c, 1), F(c, 2), F(c, 3)]]
(%i2) setify (flatten (%));
(%o2) @{F(a, 1), F(a, 2), F(a, 3), F(b, 1), F(b, 2), F(b, 3), 
                                       F(c, 1), F(c, 2), F(c, 3)@}
(%i3) map (lambda ([L], apply (F, L)), 
@c                     cartesian_product (@{a, b, c@}, @{1, 2, 3@}));
(%o3) @{F(a, 1), F(a, 2), F(a, 3), F(b, 1), F(b, 2), F(b, 3), 
                                       F(c, 1), F(c, 2), F(c, 3)@}
(%i4) is (equal (%, %th (2)));
(%o4)                         true
@end example

@opencatbox{Категории:}
@category{Применение функций}
@closecatbox
@end deffn

