Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
7.5. ФОРМАТ $HOROLOG 481
s Year=Y,Month=M,Day=D+1 q
А вариант кодирования значений года, месяца и дня в соответству­ющее значение по отсчету системной переменной $HOROLOG на языке MUMPS может быть таким:
IsLeapYear(Y)
q (Y#4=0)&((Y#100)!’(Y#400))
ENCODE(year,month,day)
n ret,i,MonthDays s ret=day,MonthDays="31,28,31,30,31,30,31,31,30,31,30,31" i $$IsLeapYear(year) s $p(MonthDays,",",2)=29 f i=2:1:month s ret=ret+$p(MonthDays,",",i) s i=year-1 q ret+(i*365)+(i\4)-(i\100)+(i\400)-672046
Приведенные функции кодирования и декодирования дат по шкале $HOROLOG оперируют также и отрицательными значениями по шкале $HOROLOG и используются на языке C в системе MiniM. В случае если применяемая MUMPS система по каким-либо причинам не поддержива­ет работу с отрицательными значениями дат по шкале $HOROLOG, но это необходимо в прикладной задаче, то могут быть использованы эти функции на языке MUMPS.
Конечно, внутри системы исполнения MUMPS нет специальных ма­леньких и очень точных часов, и отсчет времени системная переменная $HOROLOG производит по текущему времени, установленному на ком­пьютере. В случае перевода даты и времени на компьютере переменная будет возвращать значение по новому отсчету. Поэтому, строго говоря, прикладная программа не должна полагать, что одно событие произошло действительно позже другого, если значение $HOROLOG стало для него больше. Но на практике именно так и поступают.
Многие современные реализации MUMPS поддерживают расширен­ные системные функции $ZDATE, $ZDATEH, $ZTIME, $ZTIMEH или аналогичные для преобразования значения даты и времени между раз­личными форматами. Для них опорным является формат $HOROLOG. Например:
USER>f i=1:1:14 w i,": ",$zd($h,i),! 1: 03/26/2012 2: 26 мар 2012 3: 2012-03-26 4: 26/03/2012 5: мар 26, 2012
482 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
6: мар 26 2012 7: мар 26 2012 8: 20120326 9: Март 26, 2012 10: 1 11: Пн 12: понедельник 13: 26 Март 2012 14: 26.03.2012
Кроме того, многие библиотеки функций написанные на MUMPS, входящие в большие программные пакеты, также оперируют форматом даты и времени, заданном переменной $HOROLOG.
Точка отсчета времени переменной $HOROLOG ведется по локально­му времени. При этом компьютеры, находящиеся в различных часовых поясах, в один и тот же момент времени имеют различные показания $HOROLOG. Для выравнивания общего отсчета по UTC нужно допол­нительно применять отсчет часового пояса по расширенным системным переменным $ZTIMEZONE и $ZTIMESTAMP. В зависимости от реали­зации MUMPS такие переменные могут отличаться именем или тот же функционал может быть выполнен в виде системных функций. Особенно важно использование единого времени по UTC при объединении в еди­ную систему территориально удаленных серверов, а также при передаче в единый центр данных из территориально удаленных серверов.

7.6 Опции устройств

Одним из препятствий для полной переносимости программ, написан­ных на MUMPS, между различными реализациями, является различная поддержка опций устройств.
Стандарт отводит поддержку типов устройств и их опций самим про­изводителям реализаций. Это приводит к тому, что в различных MUMPS системах одни и те же операции, например, открыть файл - прочитать строки - закрыть файл, выполняются различным кодом. Код отличает­ся не только различным соглашением об именовании устройств, но и различным синтаксисом указания, что является опцией.
Опции устройства (или параметры устройства) указываются после имени устройства и могут перечисляться через двоеточие. Синтаксиче­ски опция указывается согласно стандарту 1995-го года (ныне действу­ющий) так:
deviceparameters := | deviceparam |
| ( [ [ deviceparam ] : ] ... deviceparam |
7.6. ОПЦИИ УСТРОЙСТВ 483
deviceparam := | expr |
devicekeyword := name deviceattr := name
| devicekeyword | | deviceattr = expr |
Синтаксически это позволяет указать, например, такие способы:
use filedev:(truncate) use filedev:(mode="w")
При этом стандарт ставит транслятор в неоднозначное положение, как отличить в записи опции (на диаграмме это deviceparam) ключе­вое слово опции, имя локальной переменной и выражение как результат сравнения локальной переменной с другим выражением. При этом стан­дарт перечисляет альтернативу deviceparam := expr первой, отдавая ей приоритет, но в expr как раз входит, как альтернатива, имя локальной переменной.
В рекомендации стандарта, а не в сам стандарт, входит другой вари­ант определения devicekeyword и deviceattr:
devicekeyword := [ / ] name deviceattr := [ / ] name
Применение слеша для указания имени опции или ключевого слова уже позволяет транслятору определить точно, что это именно имя опции, а не локальная переменная и что это имя опции со значением параметра, а не выражение сравнения.
Примеры, приведенные выше, уже выглядят так:
use filedev:(/truncate) use filedev:(/mode="w")
При этом сложилась ситуация, что среди современных реализаций MUMPS есть такие, которые поддерживают первый вариант, и те, кото­рые поддерживают второй. К первым относится, в частности, GT.M, ко вторым Cach´e и MiniM.
В силу сложившихся особенностей различных систем и для обспе­чения совместимости с имеющимися разработками разработчики при­кладных программ должны применять методы изолирования кода, если необходимо выполнять программы на различных MUMPS системах. К ним относятся, например, такие:
484 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
1. Вынос операций с устройствами в мнемоники.
2. Выполнение операций с устройствами в команде xecute при выборе надлежащего синтаксического варианта.
3. Поддержка различных библиотечных рутин для устройств для раз­личных реализаций MUMPS.
Кроме различного синтаксиса опций, различные MUMPS системы, даже поддерживающие одинаковый способ синтаксического указания имени опции, поддерживают устройства ввода-вывода по-разному и с разными именами опций и трактовкой их значений.

7.7 $X и $Y

Системные переменные $X и $Y были введены в язык в качестве встро­енных и стандартных системных переменных с самого начала, и по сво­ему назначению отражают положение каретки ввода на терминальных устройствах. При эксплуатации MUMPS систем эти переменные, при этом, разработчики используют с большой осторожностью.
Системные переменные $X и $Y доступны и на запись и на чтение. При записи в них MUMPS система принимает меры к установке позиции каретки в указанное положение. Синтаксически эта операция выглядит так:
set $X=12 set $Y=5
При этом язык на логическом уровне не специфицирует отличие фи­зического поведения таких переменных для различных терминальных устройств. В зависимости от реального физического способа отобра­жения и типа терминального устройства MUMPS система выполняет действия, соответствующие этому типу устройства. В частности, для Windows консоли выполняется позиционирование консольного курсора
SetConsoleCursorPosition
используя функционал WinAPI, а для телнет клиентов выполняется от­сылка команд позиционирования в виде эскейп-последовательности.
Для терминальных устройств иного типа, например, для принтеров и терминалов, выполняется передача специфических для них команд по­зиционирования каретки.
7.7. $X И $Y 485
В зависимости от типа такого терминального устройства содержание эскейп-последовательности для позиционирования также, в принципе, может отличаться, хотя большинство терминальных протоколов по ба­зовым эскейп-последовательностям совпадают.
При разработке под терминальные устройства при использовании переменных $X и $Y разработчики, безусловно, должны проверить, как именно выполняется управление позиционированием на всех типах устройств, с которыми необходимо работать.
Более сложный вопрос стоит с чтением системных переменных $X и $Y. Проблема в том, что MUMPS системе не всегда может быть извест­но, где реально находится каретка ввода на терминальном устройстве после того, как была выполнена какая-либо из команд. Вообще говоря, MUMPS система может гарантировать корректный пересчет координат лишь при выполнении команды write с выводом строки, состоящей из печатных символов.
Все остальные случаи вывода уже не гарантируют корректного зна­чения $X и $Y для произвольно взятого терминально-ориентированного устройства. К таким операциям относятся операции управления форма­том
write ! write # write ?NN
операции вывода отдельного символа, даже если это код печатного сим­вола
write *code
и операция вывода с помощью эскейп-последовательностей или мнемо­ник, например
write /MNEMNAME(params)
В этих случаях, вообще говоря, система может лишь гарантировать, что управляющая последовательность была передана отображающему устройству.
При выполнении операций управления терминальным устройством через мнемоники или через явную передачу эскейп-последовательностей, вообще говоря, перед каждой мнемоникой стоит задача выполнить опе­ределенное действие, например удалить строку. При этом не для каж­дой такой мнемоники может быть гарантировано, как именно данный тип терминального устройства спозиционирует каретку по выполнению
486 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
операции. Хотя для большинства мнемоник, косвенно меняющих или мо­гущих изменить положение каретки, такое определение дано для эскейп­последовательности.
Существует довольно большое число типов терминальных устройств, отличающихся в деталях протокола управления, набором поддерживае­мых эскейп-последовательностей и побочным эффектом позиционирова­ния каретки при выполнении команд управления. И, зачастую, для кор­ректной работы с различными типами устройств, прикладные програм­мы, ориентированные на сложный терминальный вывод, поддерживают специальную библиотеку с настройками действий, которые необходимо выполнить в зависимости от типа терминального устройства.
Вообще говоря, MUMPS система может поддерживать отсчет коор­динат $X и $Y даже для нетерминальных устройств, например для тек­стовых файлов. Но и в этом случае не выполняется пересчет положения условной каретки при выводе кода символа.
Положение с чтением значений $X и $Y во многом сходно с чтени­ем значения $TEST. Общая практика состоит в том, что фрагмент кода не должен полагаться на значение такой системной переменной, если не он привел к изменению значения. И, для того, чтобы использовать переменные $X и $Y на чтение, разработчики предварительно выполня­ют явное позиционирование, и лишь затем код выполняется в условиях относительно корректного позиционирования и корректного отсчета по координатам терминального устройства.
Таким образом, разработчики, при необходимости использования зна­чений $X и $Y на чтение, должны в любом случае протестировать пове­дение кода для различных терминальных устройств, которые предстоит эксплуатировать и проверить, после каких команд вывода они могут по­лагаться на корректное значение $X и $Y, а после каких нет.

7.8 Возврат результатов

При планировании прикладных систем разработчики на MUMPS имеют возможность выбирать из множества вариантов, как именно вычисляю­щая функция или алгоритм будут возвращать результат.
Первое, на что нужно обратить внимание при выборе метода - это поддерживаемое используемыми MUMPS системами ограничение на дли­ну строки. В случае если результат вычисления гарантированно уклады­вается в этот лимит, то обычно применяется функция, возвращающая значение, и вызываемая через $$.
7.8. ВОЗВРАТ РЕЗУЛЬТАТОВ 487
При выборе варианта в виде возврата одной строки остается опре­делиться, будет ли применено структурирование результата - будет ли строка одним цельным значением, или следует применить структуриро­вание в виде строки с разделителями (для $PIECE()) или в виде списка (для $LIST()).
Если результат вычислений не укладывается в поддерживаемое си­стемой ограничение по длине, или необходимо иное структурирование, то разработчики должны выбирать из более сложных случаев. Приведем основной список возможностей вернуть результат вычисления алгорит­мом:
1. Возврат по значению ($$).
2. Возврат по ссылке в локальную переменную.
3. Запись в предопределенную переменную.
4. Возврат значений в переменную, имя которой передается косвенно.
5. Итеративный возврат.
Теперь рассмотрим варианты возврата значений и их особенности подробнее.

7.8.1 Возврат по значению ($$)

Для возврата по значению используется вызов вычисляющей функции в контексте возврата ($$) и возврат вычисленного значения командой QUIT. Простой пример возврата одной строки по значению:
ByValue(param) ; k w $$ByValue^RETURN(123) w
n ret s ret="Calculate, param = "_param q ret
При вызове просто выполняется команда QUIT с аргументом:
USER>k w $$ByValue^RETURN(123) w Calculate, param = 123 USER>
К подводным камням такого возврата можно отнести поддерживаемое используемой MUMPS системой ограничение на длину строки и возмож­ность вызова без контекста возврата командой DO. Для второго случая
488 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
у программистов есть системная переменная $QUIT, при ее равенстве 1 возврат командой QUIT с аргументом необходим, а если значение 0, то возвращать не следует, нужна команда QUIT без аргумента.
К договоренности по формату вызова в этом случае также нужно от­нести ожидаемое структурирование возвращаемой строки: будет ли это цельное значение, строка с разделителями или список. В разработках при применении строки с разделителями можно встретить два вариан­та: использование предопределенного разделителя и запись используе­мого разделителя первым символом возвращаемой строки. Второй ва­риант встречается намного реже, но обеспечивает корректную работу кода в случае смены разделителя и возможность для вызываемой функ­ции использовать наиболее подходящий разделитель в зависимости от специфики данных.
В случае использования списковой структуры (для $LIST()) нужно обязательно проверить, поддерживается ли такой функционал в целевой MUMPS системе или ее версии.
Возрат по значению обычно используется для возврата атомарных или структурированных простым способом значений, если объем возвра­та небольшой и при развитии системы и предъявляемы к ней требований этот объем также не выйдет за пределы ограничения на длину строки.

7.8.2 Возврат по ссылке

Для возврата результата вычисления по ссылке вычисляющей функции передается ссылка на локальную переменную (через точку). Могут быть переданы также несколько ссылок, на несколько переменных. Простой пример такой передачи с использованием структурирования по индексам:
RunByRef ; k d RunByRef^RETURN w
n a,b d ByRef(.a,.b) w q
ByRef(param1,param2)
s param1(1)="param1 1" s param1(2)="param1 2" s param2(1)="param2 1" s param2(2)="param2 2" q
При выполнении кода вызывающая функция передает ссылки на свои локальные переменные (фактические параметры), а вызываемая в них записывает, используя свои имена формальных параметров:
7.8. ВОЗВРАТ РЕЗУЛЬТАТОВ 489
USER>k d RunByRef^RETURN w
a(1)="param1 1" a(2)="param1 2" b(1)="param2 1" b(2)="param2 2" USER>
Возврат значений по ссылке можно свободно комбинировать с воз-
вратом по значению.
К подводным камням такого способа относится ограничение на об­щий объем памяти, занимаемый локальными переменными процесса и невозможность передать ссылку на глобал, так как этот вариант не под­держивается синтаксически.
При таком способе может быть применено произвольное структури­рование вычисленных результатов: цельная строка, строка с разделите­лями, список, структурирование по индексам, а также могут быть ис­пользованы несколько разных локальных переменных.
К плюсам или минусам (в зависимости от ситуации) может быть от­несено то, что перед вызовом вычисляющей функции в передаваемые локальные переменные уже могут быть записаны некоторые значения и они будут доступны вычисляющей функции. В случае если вызывае­мой функции необходимо гарантировать, что весь возврат в переменную принадлежит ей, перед записью нужно удалить из этой переменной все значения:
ByRef(param1,param2)
k param1,param2 s param1(1)="param1 1" ...
Возврат по ссылке обычно используется тогда, когда необходимо пе­редать больше чем одно атомарное значение, или необходимо структури­ровать ответ по индексам, но возвращаемый объем заведомо умещается в области локальных переменных процесса.

7.8.3 Запись в предопределенную переменную

Запись в предопределенную переменную применяется из-за возможно­стей языка MUMPS видеть все созданные ранее по стеку локальные переменные, если они не были экранированы командой NEW. Многие разработки на языке MUMPS, выполнявшиеся много лет назад, исполь­зовали такую возможность как за неимением команды NEW, так и для организации взаимодействия без передачи параметров на стеке.
490 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Для вычисляющей функции просто резервируется набор имен пере-
менных (локальных или глобальных), в которые ожидается возврат.
Для современных методик программирования такой способ выглядит несколько архаично, но, тем не менее, имеет свои плюсы - возможно применять произвольное структурирование.
Как пример можно привести фрагмент кода работы с датами, который часто приводится как демонстрационный по языку MUMPS:
7 S %=%H>21608+%H-.1,%Y=%\365.25+141,%=%#365.25\1
S %D=%+306#(%Y#4=0+365)#153#61#31+1,%M=%-%D\29+1 S X=%Y_"00"+%M_"00"+%D Q
; YX D YMD S Y=X_% G DD^%DT YMD D 7 S %=$P(%H,",",2) D S K %D,%M,%Y Q
Здесь даже не используются параметры меток и возврат по значению, код просто использует текущие локальные переменные на чтение или на запись. С тем же успехом он мог бы использовать и глобальные переменные. Фактически, в этом случае вызываемые метки являются частью общего вычисляющего алгоритма.
К плюсам такого способа относится практически полное отсутствие каких-либо ограничений, а к недостаткам - необходимость согласовы­вать вызывающий и вызываемый код по именам локальных переменных и несколько большая трудность чтения такого кода современными про­граммистами.
Предопределенные переменные с точки зрения классического MUMPS используются всегда, поскольку язык сам по себе определен как строчно­ориентированный и структурирование кода на функции это лишь взгляд программиста, а в самом языке нет таких синтаксических конструкций, как функция, содержащая собственные переменные функции.

7.8.4 Возврат значений косвенно

Для косвенного возврата значений вызываемой функции передается имя (или несколько имен) локальных или глобальных переменных. Вызыва­емая функция для записи результата использует это имя косвенно.
В качестве демонстрационного примера приведем такой вариант:
RunIndir ; k d RunIndir^RETURN w
n var s var("data")="123" d Indir($na(var)) w