Сделан новый оператор switch - выбор одной из многих веток исполнения:
switch (expr)
case const1 [,const2 [,const3]]
// операции
case const11
// операции
otherwise
// операции
end
Для похожести на "do case","endcase" добавлены команды "do switch","endswitch"
Сделана новая операция :=@ - присвоение по ссылке:
a:="a"
b:=@a
? a,b // "a","a"
b:="b"
? a,b // "b","b"
b:=@NIL // отрывает ссылку.
Специально для удобной работы с MAP и объектами сделан вот такой цикл:
(подробное описание о MAPах и объектах находится ниже)
FOR value IN map_var
? value
NEXT
будет перебирать все элементы в map_var и класть их в value;
или
FOR key IN map_var KEYS
? key, map_var[key]
NEXT
будет перебирать все элементы в map_var и в key складывать значения
ключей для доступа к текущему элементу MAP или объекта.
Макроподстановки компилируют и исполняют более сложные выражения и рекурсивные
вызовы макроподстановок. Вернее обрабатываются практически все конструкции,
кроме команд и операторов типа for, while и т.п.
Рациональные числа с неограниченной точностью!
Большие числа пока поддерживают только простые математические операции -+/*^%.
Чтобы задействовать данные возможности, надо просто сказать где-нибудь
в программе
set(_SET_RATIONAL,.t.)
или командой описанной в clip.chset rational on|off
Компилятор не поддерживает большие числа в качестве констант, т.е.
при использовании выражений типа x:=123456789012345678901234567890
компилятором будет сделана попытка преобразовать его в double, и чем это
закончиться - одному сопроцессору известно.
Чтобы реально использовать большие константы, надо использовать функцию VAL() x:=val("123456789012345678901234567890")
Можно и так:
x:=val("1234.567890")
x:=val("1/3")
Функция STR(val,len,dec) теперь поддерживает параметры len, dec любого
значения. Например:
? str(val("7/3"),1000,990)
Добавлена функция RSTR(val), которая выдает строку в виде
"numerator/denominator", т.е. эту строку можно поместить в val() и при этом
не получить никакой потери точности при преобразованиях! ? rstr(val("7/3")+val("5/6")) // ---->>> "19/6"
Хэш-код - число больше чем 1 000 000 000 - результат сворачивания
строки в число по методу битовых операций с каждым байтом указанной
строки. Для строк менее 20 символов вероятность получения одинаковых
кодов для разных строк примерно 1/1000000.
Для строк более длинного размера вероятноcть падает.
Компилятор понимает шестнадцатиричные числовые константы в виде
0x20, 0x30.
Ассоциативные массивы (AM) - подробнее в модели ОО.
Если коротко, то обычный массив выглядит так:
declare m[5]
m[1]=11; m[2]=12; ....
Ключами доступа к элементам являются непрерывный ряд чисел,
ограниченный объявленным размером массива, в результате чего вставить
в такой массив элемент с ключом 6 становиться проблемой и
тормозом в прикладной программе.
А у AM ключами являются строки, например:
m=map()
m:asdf=11; m:qwer=12
? valtype(m) // 'O' //object
? len(m) // 2
? m:qwer // 12
Фактически в качестве ключа доступа к элементу такого массива является
хэш-код указанной строки, который вычисляется на этапе компиляции.
Хэш-код может вычисляться компилятором еще и посредством конструкции `qwer`
Например:
? m:qwer==m[`qwer`] // .t.
В run-time хэш-код можно вычислить hashstr("QWER")? m:qwer==m[hashstr("QWER")] // .t.
обратите внимание на то, что QWER пишется большими буквами, это сделано
из-за того, что компилятор не различает регистры букв.
Получить список ключей в АМ можно mapkeys(m) - возвращает массив
с хэш-кодами, которые есть в массиве m
mm=mapkeys(m)
(вместо функции mapkeys() можно использовать специальный цикл,
описание которого находится чуть выше)
? len(mm) // 2
for i to len(mm)
? mm[i] //вернет что-то типа 1233345677, 124321423
next
Еще АМ характеризуется очень быстрым доступом к элементам, так как
фактически поиск нужного элемента производиться по бинарному дереву.
И вместо ascan(mm,`QWER`) лучше использовать `QWER` $ m // -> .t.
Модель ОО.
Для начала немного о встроенной ОО-модели клиппера.
Она построена на основе обычных массивов и любое обращение типа
obj:attribute или obj:method()
приводит к тому, что в массиве obj
производится поиск элемента, у которого первый элемент совпадает с
именем attribute или method, причем такой поиск производится линейно
и практически является аналогом функции
ascan(obj,{|x|x[1]=="attribut"}),
что естественно очень плохо сказывается на производительности
ОО-модели "чистого" CA-Clipper. Я конечно немного упрощаю для более
простого понимания, но смысл остается такой, который я описал.
Надеюсь, теперь понятно, для чего сделаны ассоциативные массивы?
ОО-модель на основе АМ работает быстрее на порядок!
При этом исчезает необходимость в конструкциях типа
obj:=tclass(class_name):new()
и в самом классе tclass, который
занимается тем, что обеспечивает run-time поддержку регистрации
новых классов. А это увеличивает эффективность ОО-модели еще
в несколько раз.
Как сделать свой класс? Очень просто:
function MyClassNew()
obj:=map() // пустой объект
clone(MyClass2New(),obj) // унаследовать структуру от MyClass2
clone(MyClass3New(),obj) // унаследовать структуру от MyClass3
// если уже имеются совпадающие атрибуты
// или методы, то они будут заменены по
// принципу "кто последний - тот и папа"
obj:attribute1:=0
obj:attribute2:=date()
obj:method1:=@func1() // присвоить методу указатель на функцию
obj:method2:=@func2() // эти функции должны быть определены в этом же
// prg-файле
// если методы уже были унаследованы от других
// классов - они будут переназначены на указанные.
return obj // вернуть готовый объект
static function func1
::attribute1++
return NIL
static function func2(self)
self:attribute1--
return self
Хотелось бы еще добавить два простых правила:
атрибут рождается при первом присвоении в него чего-нибудь, в том
числе и NIL;
методом можно назначить или переназначить в любой момент в run-time
любую функцию, объявленную как static function в этом модуле.
Либо наследовать ее от другого объекта, как обычное присвоение значений.
myObj1:metod1 := myObj2:=metodX
Как использовать объекты? Еще проще, вернее так же, как и в CA-Clipper.
obj:=MyClassNew()
obj:metod1()
? obj:attribute1
В объекте можно объявить метод destroy(), но это не совсем destructor,
как это принято в языках 3 поколения. Есть переменная
local myObj:=myclassNew()
В ней лежит объект.
При выходе из тела функции эта переменная будет уничтожена со всеми
вложенными в ней данными.
Берем пример, что в этой myObj имеется атрибут
myObj:hFile:=fopen(trali_vali).
При уничтожении myObj необходимо закрыть hfile, но компилятор
об этом не знает, компилятор (вернее не компилятор, а виртуальная машина)
знает только о том, что в hFile лежит число и уничтожит только число,
а файл останется открытым!
Вот для этого и делается метод destroy, который будет вызываться
(если таковой имеется) перед уничтожением переменной myObj.
static function my_destroy()
fclose(::hFile)
return
Контроль изменений аттрибутов в объекте.
Если надо контролировать изменения атрибутов объекта, то сделайте
метод modify и установите mapmodify(map_obj, .t. ) . Тогда
все изменения будут приходить сначала в метод modify и, только после
этого, изменится значение атрибута, на то значение которое вернет
метод modify()
CLIP умеет хранить в MEMO полях данные любого типа, в том числе и объекты.
Но при записи объекта в БД не сохраняются методы объекта (потому что нет смысла
хранить код с каждым экземпляром, да и методы имеют тенденцию к изменению).
А при восстановлении объекта происходит следующее: раскодируюся данные ( var ),
если тип полученных данных - объект, то у объекта запрашивается атрибут
CLASSNAME и делается попытка вызвать функцию _recover_&var:CLASSNAME(var),
в которую передается восстановленный объект. Эта функция и должна назначить
"утерянные" при записи методы.
Этим же механизмом можно пользоваться и для передачи объектов в виде строки.
Например по почте или TCP-соединению :)
Вот пример использования:
x:=asdfNew() /* создается объект */
? "x:m1",x:m1() /* проверка работоспособности */
? "x:m2",x:m2()
y:=var2str(x) /* объект кодируется в строку */
/* or field->memo_field:=x */
? "y=",y
z:=str2var(y) /* раскодируется обратно, при этом вызывается _recover_asdf() */
/* or z:=field->memo_field */
? "z=",z
? "z:m1",z:m1() /* проверка на работоспособность */
? "z:m2",z:m2()
?
return
function asdfNew()
local o:=map()
o:classname := "ASDF"
o:a1 := "asdf"
o:a2 := "qwer"
_recover_asdf(o)
return o
function _recover_asdf(o)
o:m1 :=@ asdf_1()
o:m2 :=@ asdf_2()
? "recovering"
return o
static function asdf_1
? "asdf_1",::a1
return ::a1
static function asdf_2
? "asdf_2",::a2
return ::a1
Благодаря такому устройству ОО-модели и возможности компилировать
быстрый код посредством трансляции в С-программу, появилась возможность
написать стандартные классы TBrowse, Get на самом CA-Clipper.
При этом визуально производительность этих классов не хуже, чем
написанные на чистом C в стандартном CA-Clipper.