После этого, согласно требуемому алгоритму модификации строка P1 обрабатывается средствами, ориентированными на обработку строк, включая и эффективные процедуры, представленные нашей библиотекой [103,109], давая в результате P2-строку. И на заключительном этапе по вызову eval(parse(P2)) получаем результат вычисления P2-выражения, доступный в текущем сеансе. Нижеприведенный текст процедуры Aproc [109], генерирующей расширенные процедуры, весьма прозрачен и превосходно иллюстрирует описанный выше механизм герерации выражений посредством parse-функции:
Aproc := proc(F::symbol, A::list({symbol, `::`}), p::integer) local a b c d, , , ; assign(a = convert(eval(F), 'string')), assign(b = op 1,( eval(F))), assign(c = "(" ( || seqstr(b)) || ")" ;) if A = [ ] then parse( )a elif b = NULL then d := "(" || (seqstr(op(A))) || ")"; eval(parse(sub_1(c = d, a))) elif p ≤ 0 then d := "(" ( || seqstr(op(A), b)) || ")"; c := "(" || (seqstr(b)) || ")"; eval parse(( sub_1(c = d a, ))) |
elif nops([b]) ≤ p then d := "(" || (seqstr(b, op(A))) || ")"; c := "(" || (seqstr(b)) || ")"; eval parse(( sub_1(c = d a, ))) else d := "(" || (seqstr(op([b][1 .. p]), op(A), op([b][p + 1 .. -1]))) || ")"; c := "(" ( || seqstr( ))b || ")"; eval parse(( sub_1(c = d a, ))) end if end proc > Kr:= proc(x, y, z, h) `+`(args) end proc: Aproc(Kr, [a, b, c, t::string, v, r::list(symbol), w], 2); proc(x, y, a, b, c, t::string, v, r::list(symbol), w, z, h) `+`(args) end proc |
Для возможности представления конструкций в P1-строке можно эффективно импользовать для их программирования такие средства как assign, assign67, seq, add и целый ряд других. Достаточно эффективным средством выступает использование непоименованных процедур. В частности, посредством этих средств мы имеем возможность программировать однострочные экстракоды, реализующие достаточно сложные алгоритмы. Немало примеров этому можно найти в книге [41,103]. Сложность таких конструкций определяется как опытом, так и навыками пользователя. Между тем, указанный подход имеет целый ряд ограничений и в этом отношении более универсальным является предложенный нами метод «дисковых транзитов» [41,42,103], широко используемый нами и в других системах программирования.
Несомненным преимуществом метода «дисковых транзитов» является и то, что он распространяется на многие программные системы, не располагающие эффективными аналогами parse-функции, в частности, Mathematica, MathCAD и др. Поэтому, многие процедуры, использующие данный метод, существенно проще погружаемы в программную среду таких средств. Таким образом, средства нашей Библиотеки используют оба указанных метода при явном приоритете второго. Учитывая характеристики современных ПК (RAM, HDD, тактовая частота), а также их ближайшие потенции, оценивать эффективность обоих методов, на мой взгляд, задача достаточно неблагодарная. Однако, пользователь в качестве весьма полезных упражнений может поставить перед собой задачу привести все (или выбранные) процедуры нашей библиотеки [41,103,109] к единому методу. В принципе, представленные в ней средства далеко не всегда оптимизировались в строгом понимании этого понятия, хотя оценки эффективности для целого ряда из них и проводились. Нами таких целей не ставилось, ибо многие средства писались (что называется с листа) за один присест. Между тем, 4-летний период использования всех трех версий Библиотеки во многих научно-исследовательских организациях и университетах подтвердили вполне достаточную эффективность библиотеки как в качестве дополнения к пакету Maple, так и в качестве полезного учебного материала по курсу программирования в среде пакета. Надеемся, что и читатель найдет для себя кое-что полезное при освоении программной среды пакета Maple.
К parse-функции непосредственно примыкает и группа из двух функций sscanf, sscanf и процедуры scanf, имеющих следующие три формата кодирования:
(1) sscanf("<Строка>", "<Формат>") (2) fscanf(<Файл>, "<Формат>")
(3) scanf("<Формат>")
и предназначенная для обеспечения синтаксического анализа сканируемого содержимого строки, базирующегося на заданной форматирующей строке (формат), в соответствии с синтаксисом Maple. В этом отношении sscanf-функция сочетает возможности рассмотренной функции parse и printf-функции, рассматриваемой ниже. При этом, в качестве фактических значений string-типа для аргументов всех трех средств могут выступать и значения типа {symbol, name}.
Функция sscanf сканирует указанную Строку [конвертируя входящие в нее числа и подстроки согласно заданной форматирующей строки-конвертора (Формат)] и осуществляет их грамматический анализ в соответствии с Maple-синтаксисом, возвращая список сканированных компонент указанной первым аргументом строки. Форматирующая строка состоит из конвертирующих %-спецификаторов, имеющих следующую довольно простую структуру кодирования:
%{*} {<Длина>} <Код>
напоминающую структуру форматирующих %-спецификаторов рассматриваемой ниже процедуры printf. Здесь необязательный (*)-параметр указывает на то, что сканируемая компонента строки не должна появляться в составе возвращаемого sscanf-функцией результата. Параметр Длина определяет максимальную длину сканируемой части компоненты строки, которой соответствует данный %-спецификатор. Это позволяет выделять для конвертации и анализа подкомпоненты данной компоненты. Наконец, параметр “Код” определяет как тип сканируемой компоненты строки, так и тип возвращаемого элемента в выходном списке; его допустимые значения определяет следующая табл. 9.
Таблица 9
Код | Смысл: следующий сканируемый символ (не пробел) относится к: |
d | десятичному целому числу со знаком или без; возвращается целое число |
o | целому 8-ричному числу без знака; возвращается как десятичное целое |
x | целому 16-ричному число без знака; возвращается как десятичное целое |
{e|f|g} | десятичному числу со знаком или без; возможно с десятичной точкой либо в научной нотации с {E|e}-основанием; возвращается как число float-типа |
s | строчному типу (внутри не допустимы пробелы); возвращается Maple-строка |
a | невычисляемому Maple-выражению (не должно включать пробелы) |
c | строчному значению; длина-параметр определяет его длину при возврате |
[...] | символы между [..]-скобками рассматриваются как элементы списка и возвращаются в виде строки; если список начинается с (^)-символа, все элементы списка игнорируются; если список содержит ]-скобку, она должна следовать сразу за [-скобкой, если список не начинается с (^)-символа; (-)-символ может использоваться в качестве указателя диапазона символов, например: A-H |
m | сканируется целиком Maple-выражение, заданное в формате m-файла; длинапараметр игнорируется; выражение возвращается невычисленным |
{he|hf|hg} | 1- или 2-мерный массив {float, integer}-чисел; возвращается hfarray-значение |
hx | 1- или 2-мерный массив чисел float-типа в 16-ричном IEEE-формате; возвращается 1- или 2-мерный массив hfarray-значений |
n | возвращается как целое общее число сканируемых до "%n" символов |
Следует иметь в виду, что непустые символы между %-спецификаторами форматирующей строки игнорируются, но должны по типу отвечать соответствующим символам сканируемой (считываемой/вводимой) строки. Функция fscanf (второй формат) отлична от sscanf-функции только тем, что сканируемая строка читается из файла, заданного его спецификатором (путь к нему в файловой системе ПК) в качестве фактического значения ее первого аргумента и открываемого как файл текстового формата. Функция fscanf читает строки файла целиком, но использует ровно столько символов, сколько требуется для обеспечения всех конвертирующих %-спецификаторов ее форматирующей строки. Остающиеся символы доступны для следующего вызова функции, так как файл остается открытым и указатель установлен на следующий считываемый символ.
При использовании {he|hf|hg}-кода следует иметь в виду, что сканируемые символы классифицируются на три типа: числовые, разделители и ограничители. К числовым относятся: цифры, десятичная точка, знаки (±) и символы {e, E, d, D}. Символы пробела, запятые или квадратные скобки полагаются разделителями, остальные - ограничителями. При этом, символ слэша "/" является идентификатором конца сканирования, а не символ обратного слэша "\", как указано в документации. Более того, в условиях Windowsплатформы недопустимыми являются форматирующие {D, O, X}-коды.
Наконец, третий формат (scanf) читает символы из стандартного входа и эквивалентен функции fscanf(<Стандартный вход>, <Формат>). В качестве стандартного входа по умолчанию полагается ввод с консоли (клавиатуры) ПК. Только успешно сканированные компоненты строки возвращаются в качестве элементов выходного списка, в противном случае возвращается пустой список,т.е. []. Если при сканировании компонент не обнаружено соответствующих форматирующей строке и реально достигнут конец ввода, то возвращается нулевое значение. Это же значение возвращается, если считывается пустой файл. Ряд сделанных ниже замечаний относительно форматирующей printf-процедуры сохраняет силу и для sscanf, fscanf и scanf. Следующий комплексный фрагмент иллюстрирует применение средств sscanf-группы для сканирования и синтаксического анализа строчных и символьных Maple-конструкций: