Следует отметить, что в таблицах главы и в последующих не приводится исчерпывающей характеристики функций либо других средств Maple-языка, а только основные их назначение и аргументы. Некоторые их особенности приводятся в прилож. 3 [12], полную же информацию по любой функции, поддерживаемой пакетом, можно оперативно получать по конструкции ?<функция> или в документации по Maple, например, [8183,89]. Следует еще раз напомнить, что кодирование идентификаторов в пакете Maple регистро-зависимо, поэтому необходимо правильно кодировать все используемые идентификаторы. Несоблюдение данного условия лежит в основе многих ошибок.
Для решения задач математической логики, пропозиционального исчисления, а также организации логических конструкций, управляющих вычислительным процессом в документе Maple либо программе (условные переходы, ветвления, циклические и итеративные вычисления и др.), Maple-язык располагает рядом встроенных, библиотечных и модульных процедур и функций, операторов и иных конструкций, значения аргументов или возвращаемых результатов которых получают логические значения true (истина), false (ложь) и FAIL (неопределенность). К группе данных средств относятся и так называемые тестирующие функции и процедуры. Такие средства в зависимости от результата тестирования своего аргумента возвращают значение true или false. Ряд свойств таких функций нами рассматривался выше в связи с другими вопросами Maple-языка, остальные будут рассматриваться здесь и в последующих разделах по мере необходимости и в контексте с различными вопросами практического программирования.
В основе математической логики, поддерживаемой Maple-языком, лежит понятие булевого (boolean; логического) выражения. Как уже отмечалось, Maple-язык поддерживает трехзначную логику {true, false, FAIL} для всех булевых операций. Булевы выражения формируются на основе базовых логических операторов {and, or, not}, образующих функционально полную систему (в смысле возможности представления на их основе произвольной логической функции) и операторов отношения {< (меньше)|<= (не больше)|> (больше)|>= (не меньше)|= (равно)|<> (не равно)}. Булевский (boolean) тип идентифицируется тестирующими функциями type и typematch, и процедурой whattype; и при этом, Maple-язык дифференцирует boolean-тип на два базовых подтипа: relation и logical. К relation-подтипу относятся выражения вида {<|<=|=|<>}, а к logical-подтипу - выражения вида {or, and, not}; тогда как boolean-тип определяют как собственно выражения двух указанных подтипов, так и их сочетания, а также логические константы {true, false}. Все логические типы тестируются {type|typematch}-функцией следующего формата кодирования:
{type|typematch}(<Выражение>, {boolean|relation|logical})
как это иллюстрирует следующий простой пример:
> [type(AV <> 64, 'relation'), type(AV and AG, 'logical'), type(true <> false, 'boolean')],
[whattype(AV and AG), typematch(AV <> 59, 'relation')]; ⇒ [true, true, true], [and, true] Для вычисления Maple-выражений в булевой трактовке используется evalb-функция, имеющая простой формат кодирования: evalb(<Выражение>) и возвращающая логическое значение {true|false|FAIL}; если это невозможно, то выражение возвращается невычисленным. Основной задачей evalb-функции является вычисление Maple-выражений, содержащих операторы отношения, в терминах логических значений. Это необходимо в целом ряде случаев, связанных с различного рода задачами анализа вычислительных конструкций, ибо Maple-язык трактует выражения, содержащие операторы отношения, как алгебраические уравнения или неравенства, если выражения дополнительно не содержат логических {and, or, not}-операторов. И только в качестве аргументов evalb-функции либо в {if|while}-предложениях Maple-языка они получают логическую трактовку. При этом, следует иметь в виду, что Maple-язык конвертирует выражение, содержащее операторы отношения {>, >=}, в эквивалентное ему выражение в терминах {<, <=}-операторов. Более того, т.к. evalb-функция не производит упрощения выражения-аргумента, то в ряде случаев ее применение может приводить к некорректным результатам. Поэтому, перед вызовом evalb-функции рекомендуется предварительно упрощать выражение, передаваемое ей в качестве фактического аргумента; это можно, в частности, делать и по simplify-функции. Простой фрагмент иллюстрирует вышесказанное:
> whattype(AVZ = AGN), evalb(AVZ = AGN), evalb(AVZ = AVZ); ⇒ =, false, true
> AV:= 64: whattype(AV <= 64), evalb(AV = 64), evalb(AV <= 64); ⇒ <=, true, true > whattype(sqrt(64) <> ln(17)), evalb(sqrt(64) <> ln(17)); ⇒ <>, true
Следует иметь в виду, что вычисление логических выражений подчиняется следующему правилу: первым вычисляется левый операнд {and|or}-оператора и вычисление правого его операнда производится только тогда, когда логическое значение левого операнда может способствовать получению true-значения выражения в целом. Так, правый операнд логического and-выражения следующего вида:
> G:= 0: V:= 17: if (G = 64) and (V/G >= 0.25) then printf(`%3s\%4f`, `G=`, V/G) end if;
> G:= 0: V:= 20: if (G = 52) or (V/G >= 9.47) then printf(`%3s\%4f`, `G=`, V/G) end if; Error, numeric exception: division by zero не вычисляется и не вызывает ошибочной ситуации “деления на нуль”, т.к. левый его операнд (G = 64) для G = 0 при вычислении возвращает false-значение, не способствующее получению true-значения and-оператора в целом, независимо от результата вычисления его правого операнда, вызывающего на данном G-значении указанную ошибочную ситуацию. Иная ситуация, как показано, имеет место для случая or-оператора.
Правила выполнения логических {and,or,not}-операторов на {true, false, FAIL}-значениях в качестве операндов определяются следующими таблицами истинности:
and | true | false | FAIL | or | true | false | FAIL | not | |
true | true | false | FAIL | true | true | true | true | true | false |
false | false | false | false | false | true | false | FAIL | false | true |
FAIL | FAIL | false | FAIL | FAIL | true | FAIL | FAIL | FAIL | FAIL |
Смысл приведенных правил достаточно прозрачен и пояснений не требует, например: > [FAIL and true, false or FAIL, true or FAIL, not FAIL]; ⇒ [FAIL, FAIL, true, FAIL]
Следует отметить, что до 6-го релиза Maple для расширения круга решаемых задач математической логики и ее приложений дополнительно предоставлял 10 модульных функций, поддерживаемых средствами logic-модуля. Однако, после нашей принципиальной критики ряда его функций, точнее результатов вызовов bequal-функции на FAIL-значениях [10-12], данный модуль был исключен из Maple. И аналога этого (в целом весьма полезного) модуля не было до Maple 10. И только в последнем релизе появился модуль Logic, чьи средства предна-начены для работы с выражениями, используя двузначную булеву логику, т.е. без FAIL-значения. По конструкции ?Logic читатель может детально ознакомиться со средствами данного модуля.
Bit := proc( B::{string symbol, , list(integer)}, bits::{procedure, list {( equation integer, })}) local a b c k, , , ; c := x → [seq(parse(x k[ ]), k = 1 .. length( )x )]; if type(B, {'symbol', 'string'}) and length(B) = 1 then a := c(cat("", convert(op(convert(B, 'bytes')), 'binary'))); b := [0 $ (' 'k = 1 .. 8 − nops(a)), op(a)] elif type(B, 'list'('integer')) and nops(B) = 1 and belong(B[1], 0 .. 255) then a := c(cat "",( convert(op(B), 'binary'))); b := [0 $ (' 'k = 1 .. 8 − nops(a)), op(a)] |
else error "1st argument must be symbol/string of length 1 or integer list, but \ as received <%1>", B end if; if type(bits, 'list'('integer')) and belong {( op(bits)}, 1 .. 8) then [seq(b k[ ], k = {op(bits)})] elif type(bits, 'list'('equation')) and belong({seq(lhs(k), k = bits)}, 1 .. 8) and belong {( seq(rhs( )k , k = bits)}, 0 .. 1) then seq(assign(' 'b[lhs(bits k[ ])] = rhs(bits k[ ])), k = 1 .. nops(bits)); convert(parse cat(( op map(( convert b, , 'string')))), 'decimal', 'binary') elif type(bits, 'procedure') then convert(parse(cat(op(map(convert, bits(b), 'string')))), 'decimal', 'binary') else error "2nd argument <%1> is invalid", bits end if end proc > Bit(`S`,[k$k=1..8]),Bit([59],[1,3,8]), Bit([59],[2=1,4=0,8=0]),Bit("G",[6]),Bit("0",[6=1,7=1,8=1]); [0, 1, 0, 1, 0, 0, 1, 1], [0, 1, 1], 106, [1], 7 > R:=(L::list) -> [L[nops(L)-k]$k=0..nops(L)-1]: Bit("G", [k$k=1..8]), Bit("G", R), Bit("S", R), Bit([59], R); ⇒ [0, 1, 0, 0, 0, 1, 1, 1], 226, 202, 220 |
Для обеспечения логических операций и по-битной (поразрядной) обработки информации нами также был определен ряд процедур и модулей [42,103,109]. Так вышеприведенная процедура Bit(B, bits) обеспечивает выполнение следующих базовых по-битных операций с символом, указанным первым аргументом В (в качестве В могут выступать 1-элементные строка либо символ, а также 1-элементный список, чей элемент определяет десятичный код символа из диапазона 0..255):
(1) если второй аргумент bits имеет вид [n1, n2, …], вызов процедуры Bit(B, bits) возвращает значения битов символа B, расположенных в его позициях nj (nj лежит в 1 .. 8); (2) если второй аргумент bits имеет вид [n1 = b1, n2 = b2, …], возвращается десятичный код символа, полученного заменой значений битов символа В (в его позициях nk) на бинарные значения bk (nk = 1..8; bk = {0|1}).
(3) если второй аргумент bits – имя процедуры, определенной над списками, возвращается результат применения процедуры к списку значений всех битов символа В.
Наконец, в двух последних случаях возвращается десятичный код символа – результата обработки исходного символа В.
Базовые средства поддержки булевой алгебры обеспечиваются модулем boolop, а также нижеследующей процедурой BitWise [103,108,109]: