Object REXX. Есть ли смысл?
Автор: Василий Сидоров.
Источник: Русский электронный журнал разработчика
Некоторое время назад я обнаружил на сайте IBM новую версию ObjectREXX для Warp3, скачал и установил её. Вообще-то у меня Warp4Rus, но до FP5, как минимум, ObjREXX в мерлине не фиксился, а исправления ошибок и новых фич хочется всем. ;) Вот и в этой версии появились новые функции для работы с кортежами (stem). Описания не было, но после короткой переписки с разработчиками я его получил, перевёл и результат был опубликован здесь.
Меня заинтересовала функция SysStemSort(), и было решено написать два ("классический" и "объектный") варианта скриптов сортировки. Принцип прост: читаем стандартный ввод в кортеж, кортеж сортируется, результат пишется на стандартный вывод. И никаких продвинутых "многопутевых слияний" ;)
Сразу скажу, что победило OO и с большим отрывом. Причина проста. Классика требует построчного ввода/вывода. В объектном есть ввод/вывод массивов (arrayin/arrayout). И хотя мне требовалось переносить строки между массивом и кортежем, суммарное время оказалось в разы меньше, чем при построчном вводе/выводе.
Вторая причина - наследование. Поскольку я писал скрипты-"пузомерки", требовалось как-то фиксировать достижения. Была сделана запись о трёх фазах (чтение/сортировка/запись) в лог-файл. ОО-вариант проще в использовании и с лёгкостью может быть применён в другом скрипте. Ещё одно достоинство: лог-файл открывается явно, без буферизации и в разделяемом режиме (Deny Write), поэтому закрываться должен явно. Реализация класса logger гарантирует закрытие файла даже при аварийном завершении скрипта.
Начнём с классического варианта. Здесь ничего необычного:
[RxSortC.cmd]
/* Sample of use SysStemSort() from ObjectREXX RexxUtil
version from 18/05/1999
*/
if RxFuncQuery(SysStemSort) then
if RxFuncAdd(SysStemSort,RexxUtil)\=0 then do
call lineout con,"Can't register SysStemSort -",
"check RexxUtil version"
exit 1
end
time=time(s)
parse version name version day month year
call wlog 'Log started by' name version '('day month year')'
call time r
buffer.0=0
do i=1 while chars()\=0
buffer.i=linein()
buffer.0=i
end
call wlog ' ' buffer.0 'lines reading in',
format(time(r),5,2)'s'
if buffer.0>0 then
if SysStemSort('buffer.')=0 then
call wlog ' ' buffer.0 'lines sorting in',
format(time(r),5,2)'s'
else do
call lineout con,'Error while sorting!'
call wlog '!Error while sorting' buffer.0 'lines'
end
else
call lineout con,'Nothing to sort'
do i=1 to buffer.0
call lineout,buffer.i
end
call wlog ' ' buffer.0 'lines writing in',
format(time(r),5,2)'s'
drop buffer.
call wlog 'Log ended, total time:' time(,time(s)-time,s)
call RxFuncDrop SysStemSort
exit 0
wlog: procedure
return lineout('RxSortC.log',,
left(date(s),4)right(date(o),6),
time() '-',
arg(1),
)
Всё просто, ничего необычного и вот результат (лучший из трёх):
1999/08/30 21:15:10 - Log started by OBJREXX 6.00 (18 May 1999)
1999/08/30 21:15:57 - 12883 lines reading in 47.27s
1999/08/30 21:16:12 - 12883 lines sorting in 15.27s
1999/08/30 21:16:21 - 12883 lines writing in 8.59s
1999/08/30 21:16:21 - Log ended, total time: 00:01:11
Разница между лучшим и худшим результатом - 3 сек.
А теперь объектно-ориентированный вариант. Сначала класс logger:
[RxLog.cmd]
/* Общедоступный класс для лог-файлов */
::class logger public subclass stream
::method init -- Автоматически вызывается new()
expose time
time=time(s) -- time доступна всем методам объекта
parse version name version day month year
self~init:super(arg(1))
self~open(write append shareread nobuffer)
self~lineout('Log started by' name version '('day month year')')
::method uninit -- Вызывается при drop и завершении скрипта
expose time
self~lineout('Log ended, total time:' time(,time(s)-time,s))
self~close
self~uninit:super
::method lineout -- проставляем времЕнную отметку
self~lineout:super(date(s,,,'/') time() '-' arg(1))
::method warning -- просто строку в stderr
.error~lineout:super(arg(1))
Cобственно скрипт сортировки
[RxSortO.cmd]
/* OO version of RxSortC sript */
if RxFuncQuery(SysStemSort) then
if RxFuncAdd(SysStemSort,RexxUtil)=\0 then do
.error~lineout("Can't register SysStemSort -",
"check RexxUtil version!")
exit 1
end
log=.logger~new('RxSortO.log')
call time r
buffer.=.stem~new -- создаём объект-кортеж
buffer=.array~new -- создаём объект-массив
buffer=.input~arrayin -- вот и весь ввод;)
buffer.0=buffer~items
do i=1 to buffer.0 -- цикл для "переноски" строк
buffer.i=buffer[i]
end
drop buffer -- освобождаем память
log~lineout(' ' buffer.0 'lines reading in',
format(time(r),5,2)'s')
if buffer.0>0 then
if SysStemSort('buffer.')=0 then
log~lineout(' ' buffer.0 'lines sorting in',
format(time(r),5,2)'s')
else do
log~warning('Error while sorting!')
log~lineout('!Error while sorting' buffer.0 'lines')
end
else
log~warning('Nothing to sort!')
buffer=.array~new(buffer.0) -- создать массив известного размера
do i=1 to buffer.0
buffer[i]=buffer.i
end
drop buffer.; buffer.0=buffer~items
.output~arrayout(buffer)
drop buffer
log~lineout(' ' buffer.0 'lines writing in',
format(time(r),5,2)'s')
call RxFuncDrop SysStemSort
::requires RxLog
А вот и результат (худший из трёх):
1999/08/30 21:17:16 - Log started by OBJREXX 6.00 (18 May 1999)
1999/08/30 21:17:22 - 12883 lines reading in 6.37s
1999/08/30 21:17:31 - 12883 lines sorting in 8.86s
1999/08/30 21:17:34 - 12883 lines writing in 2.66s
1999/08/30 21:17:34 - Log ended, total time: 00:00:18
Разница лучший/худший - одна секунда.
Как можно видеть, меньшее время требуется не только на ввод/вывод, но и на сортировку. Объяснения этому факту у меня нет, но разница устойчивая. Тестировалось всё это на AMD-K6-2/300/64Mb.
В общем, ObjectREXX мне нравится. На очереди освоение start/reply/guard -- основы для написания многопоточных скриптов и скриптов-демонов.
Примечание: для переключения между обычной и объектной версиями REXX используйте команду SWITCHRX