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