Extended Evaluate, v3.0. Oleg A. Rudenko, 2003-10 ======================== Oleg_Rudenko@mail.ru ======================== Данный класс позволяет значительно расширить как функциональность стандартного оператора EVALUATE, так и значительно снизить время выполнения Evaluate-выражений. Особенно актуально, когда необходимо использовать Evaluate-выражения в циклах. Для версий Клариона C50/C55 выигрышь в скорости составляет примерно от 2 до 5 раз, в зависимости от Evaluate-выражения. В Кларионе версии C60 появился новый оператор BINDEXPRESSION, который позволяет производить предварительную подготовку Evaluate-выражения и, таким образом, снижает время выполнения его оператором EVALUATE. Но даже и в этом случае связка методов Prepare/Run данного класса работает быстрее стандартной связки BINDEXPRESSION/EVALUATE примерно на 25%-30%! Причем, с увеличением кол-ва BIND-переменных в текущей BIND-области этот выигрыш еще больше. Расширение функциональности заключается в возможности использования выражений присваивания, когда результат выражения присваивается переменной, заданной в левой части выражения, перед знаком "=". Если использовать такое выражение в стандартном операторе EVALUATE, то это выражение будет рассматриваться как обычный логический IF-оператор. Данный класс позволяет использовать как стандартное толкование таких выражений, так и рассматривать их как операторы присваивания. Кроме этого, данный класс позволяет создавать новые implicit- переменные, которые могут быть использованы как обычные переменные посредством их BIND-имен. Таким образом, данный класс позволяет создавать простые самодо- статочные (в плане используемых переменных) интерпретаторы! Для Evaluate-выражений, вычисляемых данным классом, актуальны ВСЕ требования, описанные в Clarion-help для оператора EVALUATE. Так как данный класс ПОЛНОСТЬЮ СОВМЕСТИМ со стандартным оператором EVALUATE, то он АБСОЛЮТНО КОРРЕКТНО может вычислять ВСЕ выражения, которые может вычислить оператор EVALUATE. В качестве примера использования данного класса рассмотрим упрощенный вариант метода Scan для класса TDynaFileClass из библиотеки DynaLib. Напишем процедуру обработки ЛЮБОГО файла по заданным правилам. Для простоты предположим, что все операторы в параметре _Commands разделены символом ";" и этот символ не встречается в самих операторах. Так-же предполагаем, что если заданы выражения фильтра _Filter и брейк-условия _Break, то они возвращают 1 в качестве условия их выполнения. Процедура возвращает кол-во обработаных записей файла. Если файл не открыт, то он будет открыт и будет установлен файловый порядок обхода записей. При этом, после завершения обработки, файл будет закрыт. Если файл уже открыт, то предполагается, что порядок обхода записей так-же уже установлен. Предполагается, так-же, что буфер записи файла и все, используемые в выражениях, внешние переменные уже обьявлены через оператор BIND. Дополнительно в выражениях можно использовать счетчик обработаных записей файла, обьявленный под именем "PROCESS::Counter". MAP ScanFile(FILE _File,STRING _Filter,STRING _Break,STRING _Commands),LONG,PROC END ScanFile PROCEDURE(FILE _File,,,) Counter LONG(0) evlFilter &TExtEvalClass evlBreak &TExtEvalClass evlCommands QUEUE Command &TExtEvalClass END Code if _File &= Null then Return(0). OpenFile# = Status(_File) if ~OpenFile# Share(_File); if ErrorCode() then Return(0). Set(_File) . Bind('PROCESS::Counter',Counter) if _Filter evlFilter &= New(TExtEvalClass) if ~evlFilter.Prepare(_Filter) then Dispose(evlFilter). . if _Break evlBreak &= New(TExtEvalClass) if ~evlBreak.Prepare(_Break) then Dispose(evlBreak). . Free(evlCommands); P# = 1 Loop While P# <= Size(_Commands) E# = InString(';',_Commands,1,P#); if ~E# then E# = Size(_Commands)+1. evlCommands.Command &= New(TExtEvalClass) if ~evlCommands.Command.Prepare(_Commands[(P#):(E#-1)],True) Dispose(evlCommands.Command) . P# = E# + 1 . Loop Next(_File); if ErrorCode() then Break. if ~(evlBreak &= Null) AND (evlBreak.Run() = '1') then Break. if ~(evlFilter &= Null) AND (evlFilter.Run() <> '1') then Cycle. Loop Command# = 1 to Records(evlCommands) Get(evlCommands,Command#) if ~(evlCommands.Command &= Null) then evlCommands.Command.Run(). . Counter += 1 . Dispose(evlFilter) Dispose(evlBreak) Loop Ndx# = 1 to Records(evlCommands) Get(evlCommands,Ndx#) Dispose(evlCommands.Command) . Free(evlCommands) UnBind('PROCESS::Counter') if ~OpenFile# then Close(_File). Return(Counter) Примеры использования данной процедуры: ! ! Подсчитать сумму всех оплаченных ордеров, выписаных за 2001 год. ! Номера всех таких ордеров собрать в строку, которая будет использована ! в качестве источника для отображения в листбоксе. ! Sum DECIMAL(11,2) PayDocs STRING(1024) ... Clear(Sum); Bind('Sum',Sum) Clear(PayDocs); Bind('PayDocs',PayDocs) Open(Docs); Bind(DOC:Record) Clear(DOC:Record,-1); DOC:Date = Date(1,1,2001) Set(DOC:Date_Key,DOC:Date_Key) R# = ScanFile(Docs,'(DOC:Type = 1) AND (DOC:PayLabel = 1)', | 'DOC:Date => '&Date(1,1,2002),'Sum = Sum + DOC:Sum;'& | 'PayDocs = Clip(PayDocs) &''|''& Left(DOC:Number)') ?PayDocsList{PROP:From} = PayDocs Display(?PayDocsList) ! ! Просто подсчитать кол-во клиентов, которые проживают в Москве. ! FromMoscow# = ScanFile(Clients,'Upper(Clip(Left(CLI:City))) = ''МОСКВА''') Как видим, данный класс дал нам возможность быстро написать довольно простую процедуру с огромной функциональностью! Тем более, что по скорости выполнения она НАМНОГО (в несколько раз) опережает вариант со стандартной EVALUATE() и практически не отстает от варианта с использованием явного кода! Если-же брать во внимание функциональность, то эта процедура позволяет исполь- зовать операторы присваивания, что невозможно для варианта с EVALUATE(), и намного более гибче варианта с явным кодом, когда для каждого варианта обработки приходится писать свою отдельную процедуру. !------------------------------------------------------------------------------- !------------------------------------------------------------------------------- Очередь BIND-имен, зарегистрированных в программе. Данная очередь заполняется методом GetBindNameList. Так как в очереди присутствует ANY-поле, то такая очередь должна очищаться по правилам, описанным в Clarion-help. Для этого можно использовать метод FreeBindNameList. TBindNameList QUEUE,TYPE ItemName STRING(MAXEVALNAME) ! BIND-имя ItemType BYTE ! Тип ячейки (см. ITEMTYPE-константы) VarType BYTE ! Тип переменной (при ItemType=ITEMTYPE:VAR) Var ANY ! Реферал на переменную (при ItemType=ITEMTYPE:VAR) FuncAddr LONG ! Адрес функции (при ItemType=ITEMTYPE:FUNC) END Следует иметь в виду, что адрес функции (поле FuncAddr) заполнено ТОЛЬКО для пользовательских функций, которые обьявлены как BIND-функции явно в коде программы. Если данное поле равно нулю, то под этим BIND-именем обьявлена одна из стандартных Clarion-функций, типа CLIP()/LEN()/INSTRING()/SUB() и др. Эти функции обьявляются ядром самого Clarion при обьявлении первой пользовательской BIND-переменной или функции. !------------------------------------------------------------------------------- !------------------------------------------------------------------------------- hExpr LONG,PROTECTED - Запрос, обработаный методом Prepare() и готовый к выполнению методом Run(). BindListAddr &LONG,PROTECTED ! В C60 не используется - Адрес системной таблицы BIND-имен текущей BIND-области. BindStackAddr &LONG,PROTECTED ! В C60 не используется - Адрес системного стека BIND-областей. AssignMode BYTE - Режим обработки знака равенства "=" в Evaluate-выражениях. Если AssignMode = False, то "по-умолчанию" все выражения, передаваемые в метод Prepare(), рассматриваются как обычные Evaluate-выражения, синтаксис которых описан в Clarion-help. В таких выражениях знак равенства рассматри- вается как логический знак, а само такое выражение рассматривается как логическое IF-выражение. Если AssignMode = True, то "по-умолчанию" все выражения рассматриваются как Assign-выражения, в левой части которых задана переменная, которой присваивается значение полученное при вычислении правой части выражения. После создания нового экземпляра класса AssignMode = False. ВНИМАНИЕ!!! Данная переменная влияет ТОЛЬКО на работу метода Prepare() и не влияет на уже подготовленное к выполнению выражение. AssignVarName &STRING AssignVar ANY - Если Evaluate-выражение является Assign-выражением, то после выполнения метода Prepare() в AssignVar будет находится реферал-ссылка на переменную, которая задана в левой части этого выражения, а в AssignVarName будет находится имя этой переменной. Например, если задано выражение "LOC:Sum = F1:Total * F1:Price", то в AssignVar будет находится реферал-ссылка на переменную LOC:Sum, а в AssignVarName будет 'LOC:Sum'. Если в левой части выражения задан элемент массива, то в AssignVar будет реферал-ссылка на сам массив а в AssignVarName - его имя. Если в левой части выражения задана часть строки через slice-синтаксис (например LOC:Str[5:10]), то в AssignVar будет реферал-ссылка на саму строку, а AssignVarName = 'LOC:Str'. AssignVarSelect ANY - Если Evaluate-выражение является Assign-выражением, то после выполнения метода Run() в этой переменной будет находится реферал-ссылка на переменную или ее часть, куда был записан результат. Например, если задано выражение "LOC:Sum = ...", то в этой переменной будет реферал-ссылка на переменную LOC:Sum. Если в левой части выражения задан элемент массива, например "Sum[2] = ...", то в AssignVarSelect будет реферал-ссылка на этот элемент массив. Если в левой части выражения задана часть строки через slice-синтаксис, например LOC:Str[5:8], то в переменной AssignVarSelect будет реферал-ссылка именно на эту часть строки. ВНИМАНИЕ!!! Значение данной переменной определено ТОЛЬКО после выполнения метода Run(). Если в качестве индексов элементов массива или части строки заданы выражения или переменные, то значение данной переменной AssignVarSelect может менятся после каждого вызова метода Run(). AutoBindAssignVarName BYTE - Режим обработки ситуации отсутствия BIND-имени переменной, принимающей результат Assign-выражения. Если AutoBindAssignVarName = False, то "по-умолчанию" метод Prepare вернет ошибку, если имя переменной в левой части Assign-выражения не найдено в таблице BIND-имен. Это - стандартное поведение. Если AutoBindAssignVarName = True, то "по-умолчанию" при работе метода Prepare будет использоваться implicit-технология при обнаружении имен переменных в левой части Assign-выражений, которые не обьявлены в BIND-списке. Т.е. такие имена будут обрабатываться следующим образом: - будет выделена область памяти необходимого размера - выделенная область памяти будет связана через BIND-список с именем из левой части Assign-выражения - новая BIND-переменная будет внесена в список AutoBindNameList Тип новой implicit-переменной определяется по последнему символу ее имени: # - будет создана LONG-переменная размером 4 байта $ - будет создана REAL-переменная размером 8 байт " - будет создана STRING-переменная размером 255 байт Отсутствие одного из вышеперечисленных спец-символов равнозначно присутствию символа ", т.е. будет создана переменная STRING(255). Например, в метод Prepare передано выражение 'Name = F:Name & F:Desc' и в BIND-списке нет переменной с именем 'Name': - если не включен режим автосоздания implicit-переменных, то метод Prepare завершит свою работу с ошибкой 800. - если включен режим автосоздания implicit-переменных, то будет создана новая STRING-переменная размером 255 байт, после чего в список BIND-имен будет добавлено новое имя 'Name' и связано с новой созданной переменной. - дальнейшая работа с этой BIND-переменной аналогична работе с обычной BIND-переменной. После создания нового экземпляра класса AutoBindAssignVarName = False. Все implicit-переменные, созданные во время работы данного экземпляра класса, будут существовать в системе и могут быть использованы как обычные переменные ТОЛЬКО до уничтожения этого экземпляра класса. После уничтожения экземпляра класса будут уничтожены и те implicit-переменные, которые были созданы во время его работы его методами. AutoBindNameList &TBindNameList - Список BIND-переменных, которые были созданы автоматически в результате работы данного экземпляра класса. В случае необходимости, данный список может быть использован для выборки из него информации о новых переменных или может быть просмотрен методом ShowBindNames. Этот список автоматически будет уничтожен при уничтожении данного экземпляра класса. При этом будет корректно возвращена Менеджеру памяти Клариона ВСЯ память, использованная для создания новых implicit-переменных. Так-же, из текущего BIND-списка системы будут удалены ВСЕ имена, связанные с этими implicit-переменными. ВНИМАНИЕ!!! Данный список предназначен ТОЛЬКО для чтения! Не рекомендуется его ручная модификация и, тем более, его уничтожение стандартными операторами (FREE или DISPOSE)! Так-же, не рекомендуется его очистка с помощью метода FreeBindNameList! Следует понимать, что иначе будет НЕВОЗМОЖНО корректное освобождение памяти, выделенной для новых implicit-переменных! ВНИМАНИЕ!!! Используя индексы или slice-синтаксис в левой части выражения следует иметь в виду следующее: - индексы-константы или индексы-выражения, которые не содержат BIND-переменных, типа [1:5]/[1+1]/[2,ABS(5)] вычисляются на этапе подготовки выражения методом Prepare и, таким образом, не влияют на время выполнения метода Run. - индексы, которые являются BIND-переменными или представляют собой выражения с использованием BIND-переменных, типа [LOC:Index]/[Abs(Beg#):(End#)] НЕ вычисляются методом Prepare, а лишь проходят стандартную, для данного класса, предподготовку. Непосредственное их вычисление производится при выполнении метода Run. Следует так-же учитывать что их вычисление производится ПОСЛЕ вычисления самого выражения справа от знака равенства. При использовании в левой части выражения slice-синтаксиса применительно к строкам можно использовать следующее расширение стандартного синтаксиса: - если правая граница равна нулю (0), то она принимается равной длине строки. Str[5:0] - обрабатывается как Str[5:Size(Str)] Str[5:End#] - если End# = 0, то обрабатывается аналогично первому случаю. !------------------------------------------------------------------------------- !------------------------------------------------------------------------------- Prepare PROCEDURE(STRING _Expression,LONG _Assign=-1,LONG _AutoBindAssignVarName=-1),BYTE,PROC !------------------------------------------------------------------------------- Данный метод подготавливает Evaluate-выражение к дальнейшему выполнению методом Run(). Вызов данного метода уничтожает ранее подготовленное выражение вызовом метода Free(). Метод возвращает TRUE, если выражение успешно подготовлено. FALSE возвращается в случае возникновения какой-либо ошибки. Код и текст ошибки можно узнать с помощью стандартных функций Error() и ErrorCode(). - _Expression - константа или переменная строкового типа, в которой задано Evaluate-выражение. - _Assign - константа или переменная знакового числового типа, позволяющая временно "перекрыть" значение свойства AssignMode. Действие данного параметра распространяется ТОЛЬКО на время выполнения данного метода. Если _Assign = -1, то значение свойства AssignMode не "перекрывается". - _AutoBindAssignVarName - константа или переменная знакового числового типа, позволяющая временно "перекрыть" значение свойства AutoBindAssignVarName. Действие данного параметра распространяется ТОЛЬКО на время выполнения данного метода. Если _AutoBindAssignVarName = -1, то значение свойства AutoBindAssignVarName не "перекрывается". Следует иметь в виду, что если включены режимы Assign-выражения и автосоздания implicit-переменных, и имя переменной в левой части выражения присваивания не найдено в списке BIND-имен, то время выполнения данного метода будет незначительно увеличено за счет процесса создания новой переменной. Новая созданная переменная продолжает существовать и после завершения работы данного метода, до уничтожения данного экземпляра класса. Таким образом, если метод Prepare с подобным выражением используется в цикле, то ТОЛЬКО при первом проходе будет затрачено время на создание новой implicit-переменной. !------------------------------------------------------------------------------- IsReady PROCEDURE,BYTE !------------------------------------------------------------------------------- Данный метод позволяет определить готовность к вызову метода Run(). !------------------------------------------------------------------------------- Run PROCEDURE,STRING,PROC !------------------------------------------------------------------------------- Данный метод выполняет выражение, подготовленное ранее методом Prepare(). Возвращает результат вычисления. Одновременно, если AssignVar не равно NULL, записывает этот результат в данную переменную. Пример: Que QUEUE Name STRING(60) Price DECIMAL(9,2) Total DECIMAL(11,3) END Sum DECIMAL(11,2) Eval TExtEvalClass ... Sum = 0 Bind('Sum',Sum) Bind(Que) if Eval.Prepare('Sum = Sum + (Que.Price * Que.Total)',True) Loop Ndx# = 1 to Records(Que) Get(Que,Ndx#) Eval.Run() . Eval.Free() . Message('Total sum: ' & Sum) или if Eval.Prepare('Que.Price * Que.Total') Loop Ndx# = 1 to Records(Que) Get(Que,Ndx#) Sum += Eval.Run() . Eval.Free() . Необходимо заметить, что оба варианта примера приблизительно раза в 2 быстрее чем стандартный вариант использвания Evaluate: Loop Ndx# = 1 to Records(Que) Get(Que,Ndx#) Sum += Evaluate('Que.Price * Que.Total') . !------------------------------------------------------------------------------- Free PROCEDURE !------------------------------------------------------------------------------- Данный метод производит все действия, необходимые для корректного уничтожения информации, подготовленной методом Prepare(). Вообще-то данный класс спроектирован таким образом, что вызов данного метода не потребуется даже при уничтожении экземпляра класса, т.к. вызывается автоматически методом Destruct. Единственный случай, когда может потребоваться ручной вызов данного метода - освобождение памяти между двумя вызовами метода Prepare(). !------------------------------------------------------------------------------- IsArray PROCEDURE(*? _Any),LONG !------------------------------------------------------------------------------- Данный метод позволяет определить является-ли переменная, на которую ссылается параметр _Any, массивом. Если это - не массив, то метод вернет 0. Для массива метод вернет кол-во элементов в первой размерности. Например: Var STRING(10) Array LONG,DIM(5,10) Any ANY Eval TExtEvalClass ... N# = Eval.IsArray(Var) ! N# = 0 N# = Eval.IsArray(Array) ! N# = 5 ... Any &= Array N# = Eval.IsArray(Any) ! N# = 5 !------------------------------------------------------------------------------- ArrayAny::Dims PROCEDURE(*? _Array,<*LONG _Dim1>,<*LONG _Dim2>,<*LONG _Dim3>,<*LONG _Dim4>),LONG,PROC !------------------------------------------------------------------------------- Данный метод позволяет получить информацию о кол-ве элементов по размерностям массива _Array. В качестве результата метод возвращает общее кол-во элементов во ВСЕХ размер- ностях массива. Если параметр _Array не является массивом, то метод вернет 1. Дополнительно, в параметрах _Dim1..._Dim4 метод возвращает кол-во элементов в первых четырех размерностях массива. Если такой размерности в массиве нет, то в соответствующем параметре будет возвращен 0. Пример: Array LONG,DIM(5,10) ... N# = ArrayAny::Dims(Array,D1#,D2#,D3#,D4#) ! После вызова - N# = 50; D1# = 5; D2# = 10; D3# = D4# = 0 !------------------------------------------------------------------------------- Следующие два метода позволяют работать с массивами, заданными ANY-ссылками. К сожалению, в Кларионе нет стандартных способов для такой работы! Единственный легальный выход в такой ситуации - использование Evaluate: Bind('Array',ArrayAny) N# = Evaluate('Array[3]') Но использование Evaluate в некоторых случаях может быть нежелательно из-за медленной скорости ее выполнения. В таких случаях и могут пригодится следующие методы, которые работают с массивом напрямую, что благотворно сказывается на скорости их работы. Параметры _Dim1..._Dim4 позволяют адресовать необходимый элемент массива или целиком отдельную размерность. !------------------------------------------------------------------------------- ArrayAny::Get PROCEDURE(*? _Array,LONG _Dim1,LONG _Dim2=0,LONG _Dim3=0,LONG _Dim4=0),? Возвращает значение заданного элемента массива _Array. ArrayAny::Put PROCEDURE(? _Value,*? _Array,LONG _Dim1=0,LONG _Dim2=0,LONG _Dim3=0,LONG _Dim4=0) Записывает значение _Value в заданный элемент массива _Array. Пример: Eval TExtEvalClass ... Sum += Eval.ArrayAny::Get(ArrayAny,1,10) ! Sum += Array[1,10] ArrayAny::Put(Sum,SumArrayAny,1) ! SumArray[1] = Sum !------------------------------------------------------------------------------- CheckBindName PROCEDURE(STRING _Name,<*LONG _Any>),LONG !------------------------------------------------------------------------------- Данный метод позволяет найти в системной таблице BIND-имен имя _Name. Если такого имени нет, то метод вернет False. Если есть такое BIND-имя, то метод вернет тип BIND-ячейки (см. ITEMTYPE-константы). Дополнительно, если задан параметр _Any, то в этом параметре метод вернет или адрес ANY-ссылки на переменную (если тип ячейки - ITEMTYPE:VAR), или адрес пользовательской функции (0 для системной функции) (если тип ячейки - ITEMTYPE:FUNC). - _Name - константа или переменная строкового типа, задающая проверяемое BIND-имя. - _Any - необязательная переменная LONG-типа, возвращающая адрес ANY-ссылки на BIND-переменную или BIND-функцию. Пример: bName STRING(MAXEVALNAME) Eval TExtEvalClass SumAny ANY AnyAddr LONG,OVER(SumAny) ... bName = 'Sum' Type# = Eval.CheckBindName(bName,AnyAddr) if Type# = ITEMTYPE:VAR Message(Clip(bName) & ' = ' & SumAny) elsif Type# = ITEMTYPE:FUNC if AnyAddr Message(Clip(bName) & '() is user function') else Message(Clip(bName) & '() is system function') . else Message(Clip(bName) & ' is not BIND-name!') . !------------------------------------------------------------------------------- CheckBindVar PROCEDURE(*? _Var),STRING !------------------------------------------------------------------------------- Данный метод позволяет узнать BIND-имя переменной _Var. Если переменная _Var не найдена в системной таблице BIND-имен, то метод вернет пустую строку. Пример: Var STRING(10) Eval TExtEvalClass ... Message('BIND-name variable "Var" is "' & Eval.CheckBindVar(Var) & '"') !------------------------------------------------------------------------------- ChangeBindVar PROCEDURE(STRING _Name,*? _NewVar,BYTE _AddIfNotFound=True),LONG,PROC !------------------------------------------------------------------------------- Данный метод позволяет связать с существующим BIND-именем другую переменную. Он более корректно и быстро реализует стандартный вариант BIND(_Name,_NewVar). Если BIND-имени _Name не существует и параметр _AddIfNotFound = True, то будет добавлено новое BIND-имя и связано с переменной _NewVar. !------------------------------------------------------------------------------- GetBindNameList PROCEDURE(*TBindNameList _List,BYTE _AllScope=False),LONG !------------------------------------------------------------------------------- Данный метод позволяет получить список всех BIND-имен. Возвращает кол-во записей в очереди _List. Метод очищает очередь перед началом работы, вызывая метод FreeBindNameList. Если параметр _AllScope = False, то в очередь _List попадут ТОЛЬКО BIND-имена, добавленные в текущей BIND-области, открытой оператором PUSHBIND(0). Если параметр _AllScope = True, то в очередь _List будут собраны ВСЕ BIND-имена из ВСЕХ BIND-областей. - _List - очередь типа TBindNameList для приема списка BIND-имен. - _AllScope - константа или переменная числового типа, задающая реакцию метода для существующих BIND-областей. !------------------------------------------------------------------------------- FreeBindNameList PROCEDURE(*TBindNameList _List) !------------------------------------------------------------------------------- Данный метод позволяет корректно очистить очередь _List. Так как в очереди присутствует поле ANY-типа, то такая очередь не может быть очищена простым вызовом оператора Free(_List). Более подробно см. Clarion-help по ANY-типу. !------------------------------------------------------------------------------- ShowBindNames PROCEDURE(BYTE _AllScope=False) ShowBindNames PROCEDURE(*TBindNameList _List,BYTE _SortByName=False) !------------------------------------------------------------------------------- Данный метод выводит окно со списком BIND-имен и их типов. Если параметр _AllScope = True, то в списке будут выведены ВСЕ BIND-имена из всех BIND-областей. При нажатии на клавишу EnterKey или дважды на левую кнопку мыши на BIND-имени обычной переменной (не массива), будет выведено текущее значение этой переменной. Вторая форма метода выводит окно со списком BIND-имен из списка _List. Это позволяет просматривать BIND-имена из ранеесозданных и сохраненных списков, созданных методом GetBindNameList, или из списка автосозданных переменных AutoBindNameList. Второй параметр _SortByName указывает следует-ли перед отображением сортировать переданный список по BIND-именам переменных. !------------------------------------------------------------------------------- !------------------------------------------------------------------------------- Версия 3.0 + Переработка ядра библиотеки для совместимости с последними релизами C71. Версия 2.3 + Доработка ядра библиотеки для совместимости с последним релизом C62. Версия 2.2 + Добавлена возможность автосоздания новых implicit-переменных, позволяющая существенно расширить область применения данной библиотеки. + Доработка ядра библиотеки для совместимости с последним релизом C61. * Доработана документация. Версия 2.1 + Доработка ядра библиотеки для совместимости с последним релизом C60. * Доработана документация. Версия 2.0 + Полная переработка для совместимости с Clarion C60. * Доработана документация. x Исправлены незначительные ошибки. Версии 1.2-1.6 Внутренние релизы библиотеки. Версия 1.1 Официальный релиз библиотеки. !------------------------------------------------------------------------------- !-------------------------------------------------------------------------------