(1) если список – структура с четко определенным порядком элементов, заданным при его определении (при этом, ее элементы могут дублироваться), то для множество не имеет места четкое упорядочение элементов; (2) элементы множества не дублируются
Множество представляет собой структуру, элементы которой, в принципе, неупорядочены в принятом смысле (порядок ее элементов при создании в общем случае отличен от результата ее вычисления). Данная структура является аналогом математического понятия множества объектов. В отличие от списка, порядок ввода элементов множества при его определении отличается от порядка выходного множества, устанавливаемого согласно соглашениям пакета. Если в результате определения списочной структуры ее элементы только вычисляются, то в случае множества они дополнительно еще и упорядочиваются согласно адресов занимаемых ими ячеек памяти ЭВМ, а также производится приведение кратности вхождений идентичных элементов к единице. Однако можно показать, что между порядками элементов L-списка и M-множества, имеющих одинаковые длину и состав элементов, имеет место следующее определяющее соотношение (при этом, предполагается также, что L-список не имеет дублирования элементов, ибо во множестве М каждый элемент представляется в единственном экземпляре):
sort(L, 'address')[k] = M[k], где k – номер элемента объекта, например:
> L:= [h, g, s, a, x, [64, b], {y, c, z}, 59, Sv, 39, Art, 17, Kr, 10]: M:={ h, g, s, a, x, [64, b], {y, c, z},
59, Sv, 39, Art, 17, Kr, 10}: (sort(L, 'address')[k] = M[k])$k=1..14;
10 = 10, 17 = 17, 39 = 39, 59 = 59, [64, b] = [64, b], {c, z, y} = {c, z, y}, Sv = Sv, Art = Art, Kr = Kr, h = h, g = g, s = s, a = a, x = x
> sort(L, 'address') = M;
[10, 17, 39, 59, [64, b], {c, z, y}, Sv, Art, Kr, h, g, s, a, x] = {10, 17, 39, 59, [64, b], {c, z, y}, Sv, Art,
Kr, h, g, s, a, x}
> M:= { h, g, s, a, x, [64, b], {y, c, z}, 59, Sv, 39, Art, 17, Kr, 10};
M := {10, 17, 39, 59, [64, b], {c, z, y}, Sv, Art, Kr, h, g, s, a, x}
В определенных обстоятельствах данное соотношение может оказаться полезным при работе со структурами типов {list, set}. В свете сказанного во многих случаях можно рассматривать множества как упорядоченные объекты и использовать данное обстоятельство в различных приложениях. В общем же случае это не имеет места, поэтому использовать его следует весьма осмотрительно. Структуры и данные списочного типа являются весьма общим объектом и могут использоваться как самостоятельные типы данных, для которых язык располагает целым рядом средств представления и обработки, так и для организации составных вычислительных конструкций, о которых вскользь речь шла выше и по которым дополнительная информация дается ниже. Так как объекты, подобные векторам и матрицам, в среде пакета представляются списочными структурами, то ряд средств поддержки работы с первыми может быть успешно использован и непосредственно для списков, как и наоборот. В свете определения списочной структуры, она представляется весьма удобной для более компактного представления результатов вычисления выражений, как это иллюстрировалось выше. Более того, результат вычисления списка возвращает вновь списочную структуру с сохранением порядка элементов исходного списка. Рассмотрим детальнее базовые средства работы со списками. При этом, для удобства и не оговаривая отдельно, в представляемых форматах функций в качестве их L-аргументов понимается списочная структура (список) и, если не оговорено противного, также и множество с учетом указанных различий между ними. Там, где имеются различия, они будут отмечаться.
По упоминаемой выше op(L)-функции можно “снимать” списочную структуру, превращая L-список (множество) в последовательность (exprseq) его элементов, а по вызову функции nops(L) – получать число его элементов (длину списка/множества). По конструкциям вида: L[k] и L[k]:= <Выражение> соответственно возвращается к-й элемент и изменяется значение к-го элемента L-списка путем присвоения ему значения указанного выражения, тогда как по NULL-значению (L[k]:=NULL) удалять к-й элемент невозможно, т.к. возникает ошибочная ситуация с диагностикой «Error, expression sequences cannot be assigned to lists». Для этой цели следует использовать вызов subsop(k=NULL, L) или прием, представленный в разделе 1.5. Тогда как для случая М-множества вторая конструкция заменяется, например, следующими: S:= [op(M)]: S[k]:= <Выражение>: M:= {op(S)}. Наоборот, по конструкции {[<exprseq>]|{<exprseq>}} производится конвертация последовательности (exprseq) в структуру типа {list|set} соответственно или это можно сделать по рассмотренной convert-функции.
Два случая применения op-функции следует рассмотреть особо. Как уже отмечалось выше, по конструкции op(0, B) в общем случае возвращается тип B-выражения. Однако для индексированных структур и функций возвращаются их идентификаторы, тогда как для некоторых других структур, например, последовательностей (exprseq), может инициироваться ошибочная ситуация. Наконец, в случае третьего формата op-функции (раздел 1.3) первый ее аргумент op(p1, B) определяет p1-позицию выделяемого элемента 1-го (внешнего) уровня вложенности В-выражения {B1=op(p1, <Выражение>)}, p2 – позицию выделяемого элемента из В1 {B2 = op(p2, В1)} и т.д., т.е. имеет место следующее определяющее соотношение:
op([p1, p2, ..., pn], <Выражение>) ≡ op(pn, ... op(p3, op(p2, op(p1, <Выражение>)))...)
Следовательно, третий формат op-функции ориентирован на работу с вложенными списочными структурами, а в общем случае с Maple-выражениями, имеющими несколько уровней вложенности. Следует иметь в виду, что вычисления в списочной структуре ориентированы и производятся не независимо, а поэлементно слева направо (включая уровни вложенности), именно поэтому результаты вычислений можно передавать между элементами списка в указанном направлении, что весьма существенно при организации вычислений и будет использоваться нами в дальнейшем. Функции {op, nops} очень широко используются и для произвольных выражений, но для списочных структур и множеств они являются одними из базовых средств по манипулированию их элементами, включая и уровни их вложенности. Например, по конструкции L:=[op(L), <Элемент>] производится пополнение L-списка новым указанным элементом, например:
> L:=[64,59,39,10,117]: L:=[op(L),V,G,Sv,Kr,Art]; ⇒ L:= [64, 59, 39, 10, 117, V, G, Sv, Kr, Art] Данная конструкция широко используется в практическом программировании задач.
Для возможности символьной обработки уравнений, неравенств, отношений и диапазонов важно иметь средства выделения их {левой|правой} части или {начального|конечного} выражения диапазона, что обеспечивается соответственно {lhs|rhs}-функцией, имеющей простой формат кодирования, а именно: {lhs|rhs}(V), где V – Maple-выражение одного из указанных типов {relation, range}. Следующий фрагмент достаточно прозрачен и каких-либо особых пояснений не требует:
> [map(rhs, [A = B, A <>B, A <+ B, A .. B]), op(A <>B)]; ⇒ [[B, B, B, B], A, B]
> map(op, [A = B, A <>B, A <+ B, A .. B]); ⇒ [A, B, A, B, A, B, A, B]
> [op(k, A = B), op(k, A <> B), op(k, A <= B), op(k, A..B)]$k=1..2; ⇒ [A,A,A,A], [B,B,B,B] Приведенный фрагмент иллюстрирует не только эквивалентность (легко следующую из определения функций op и {lhs|rhs}, конструкций {lhs|rhs}(V) и op({1|2}, V), но и возможность по конструкции op(V) получать список, элементами которого являются левая и правая части V-выражения (уравнение, неравенство, отношение, диапазон).
Функция member, имеющая в общем случае следующий формат кодирования:
member(<Выражение> {, <Список>|, <Множество>} {, '<Идентификатор>'})
выполняет тестирование указанного списка/множества на предмет вхождения в него указанного первым аргументом выражения. При кодировании необязательного третьего аргумента (должен быть невычисленный идентификатор) ему присваивается номер позиции первого вхождения заданного выражения в список/множество, если member-функция возвращает true-значение; иначе идентификатор остается неопределенным. Данная функция весьма широко используется при практическом программировании. В качестве ее расширения была определена процедура belong [103,109], обеспечивающая тестирование принадлежности указанного выражения множеству, списку, модулю, процедуре, символу, строке и другим типам выражений. Ниже представлены ее исходный текст и некоторые примеры применения.
belong := proc(a::anything, V::{set, equation, procedure, relation, Matrix, Vector, Array, array table list `module` range string symbol, , , , , , }) `if`(a = V true, , `if` type( ,( V 'table'), member(a, expLS {( indices(V), entries(V)})), `if`( type(V, {'equation', 'relation'}), procname(a, {OP(lhs(V))}) or procname(a, {OP(rhs(V))}), `if`(member( whattype eval(( V)), {'array', 'Matrix', 'Array', 'Vector'['row'], 'Vector'['column']}) , RETURN procname( ,( a expLS convert( ,( V 'listlist1')))), `if`( type(V, 'procedure'), procname(a, map(op, [2, 6], eval(V))), `if`( type(eval(V), 'symbol') or type(eval(V), 'string'), search(V, convert(a, 'string')), `if`(type(V, `module`), member(a, V), `if`( type(V, 'range') and type(a, 'numeric'), `if`(a ≤ rhs(V) and lhs(V) ≤ a true false, , ), `if`( type(a, 'set') and type(V, {'list', 'set'}), evalb({op( )}a intersect{op(V)} = {op(a)}), `if`( type(a, 'list') and type(V, 'list'), Sub_list(a, V), `if`( type(V, 'range') and type(a, {'list'('numeric'), 'set'('numeric')}), `if`(map procname, {( op(a)}, V) = {true}, true false, ), `if`( type(V, {'symbol', 'string'}), `if`(map2(search V, , map(cat , {op(a)}, ``)) = {true}, true false, ), member(a, V))))))))))))) end proc > m:= matrix(2, 3, [a,b,c,42,96,47]): v:= vector([42,G,47,g]): a:= array(1..2, 1..3,[[42,47,67], [62,89,96]]): t:= table([c = z]): V:= Vector([61, 56, 36, 40, 7, 14]): V1:= Vector[row]([61, 56, 36, 40, 7, 14]): M:= Matrix(1..2, 1..2, [[T,S],[G,K]]): A:=Array(1..2,1..3,1..2,[]): A[2,2,2]:=42: A[1,2,2]:=47: A[2,3,2]:=67: A[2,1,2]:=89: A[2, 2, 1]:=96: gs:=[1, [67, 38],[47, 58], 6]: sv:=42 < 63: belong(47, m), belong(47, a), belong(61, V), belong(61, V1), belong(K, M), belong(67, A), belong(z, t), belong([67, 38], gs), belong(63, sv); true, true, true, true, true, true, true, true, true |
Данная процедура существенно расширяет встроенную функцию member, упрощая в целом ряде случаев программирование приложений в среде пакета.
Начиная с 8-го релиза, Maple-язык расширен новым in-оператором, имеющим формат: el in L или `in`(el, L)
где el - произвольное Maple-выражение и L - список либо множество. Назначением данного оператора является тестирование на принадлежность el к L. Результатом применения данного оператора является возврат исходного вызова в принятой математической нотации, тогда как для получения собственно результата тестирования к полученному результату следует применять evalb-функцию. Весьма простые примеры иллюстрируют вышесказанное: