Skip to content

Instantly share code, notes, and snippets.

@evshvarov
Created August 18, 2015 12:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evshvarov/9490a93154395e5dc6d7 to your computer and use it in GitHub Desktop.
Save evshvarov/9490a93154395e5dc6d7 to your computer and use it in GitHub Desktop.
ForEach case in Caché Object Script
/// Класс-итератор для коллекций.
Class test.ForEach Extends %RegisteredObject [ Final ]
{
/// Коллекция-список <s>почти</s> любого типа.
Property collection As %Collection.AbstractList [ Internal, Private, ReadOnly, Transient ];
/// Инициализация свойства <property>collection</property>.
/// <br><br> Допустимые аргументы <var>val</var>:
/// <br> <li>объект класса-наследника от %Collection.AbstractList;
/// <br> <li>список простых элементов в формате $List;
/// <br> <li>список простых элементов в формате строки. В этом случае <var>sep</var> - разделитель элементов в строке;
Method %OnNew(val, sep = ",") As %Status [ Private, ServerOnly = 1 ]
{
if $IsObject(val) {
quit:'val.%Extends("%Collection.AbstractList") $$$ERROR($$$OrefInvalid,val)
set r%collection=val
}else{
set r%collection=##class(%ListOfDataTypes).%New()
do ..collection.InsertList($select($listvalid(val):val,1:$listfromstring(val,sep)))
}
quit $$$OK
}
/// Основной метод-обработчик.
/// <br>
/// <br>Аргументы:
/// <br>
/// <br><var>func</var>:<ol><li>имя метода экземпляра класса</li><li>имя метода класса (любого)</li><li>код в формате команды <link href=/DocBook.UI.Page.cls?KEY=RCOS_cxecute>xecute</link></li><li>некоторые сокращённые команды</li></ol>
/// Примеры вызова:<example>
/// s obj=##class(test.ForEach).%New("2,3,5")
/// ; для каждого элемента коллекции будет вызван соответствующий метод класса с передачей аргументов.
/// ; Первый аргумент выходной/входной, остальные - входные, но это лишь способ соглашения.
/// ; При желании можно поменять их местами, сделать несколько выходных и т.д.
/// d obj.Do("className:methodName",.result,param1,param2,paramN)
/// ; сумма элементов (имеет смысл лишь для коллекции чисел)
/// d obj.Do("+",.result)
/// ; произведение (имеет смысл лишь для коллекции чисел)
/// d obj.Do("*",.result)
/// ; конкатенация с разделителем (имеет смысл лишь для коллекции простых типов)
/// d obj.Do("_",.result,separator)
/// ; минимум (имеет смысл лишь для коллекции простых типов)
/// d obj.Do("min",.result)
/// ; максимум (имеет смысл лишь для коллекции простых типов)
/// d obj.Do("max",.result)
/// ; среднее (имеет смысл лишь для коллекции чисел)
/// d obj.Do("avg",.result)
/// ; любой код, где el=элемент коллекции, args=переданные аргументы
/// d obj.Do($lb("s args(1,1)=args(1,1)+el"),.result) ; эквивалент "+"
/// ; вызов подпрограммы sub^prog с передачей аргументов
/// d obj.Do($lb("d sub^prog(el,args...)"),.result,param1,param2,paramN)
/// </example>
///
Method Do(func = "+", Args...) As %Status
{
#define ReturnOnError(%expr) s sc=%expr ret:$$$ISERR(sc) sc
quit:'..collection.Count() $$$OK
if func="+" {
set func=$listbuild("s args(1,1)=args(1,1)+el")
}elseif func="*" {
set func=$listbuild("s args(1,1)=args(1,1)*el")
}elseif func="_" {
set func=$listbuild("s args(1,1)=args(1,1)_args(1,2)_el")
}elseif func="min" {
set func=$listbuild("s:el<args(1,1) args(1,1)=el"),Args(1)=999999999999999
}elseif func="max" {
set func=$listbuild("s:el>args(1,1) args(1,1)=el"),Args(1)=-999999999999999
}elseif func="avg" {
set func=$listbuild("s args(1,1)=el/args(1,2)+args(1,1)"),Args=2,Args(2)=..collection.Count() kill Args(1)
}
if $listvalid(func) {
set cmd=$list(func)
$$$ReturnOnError(##class(%Routine).CheckSyntax(" "_cmd))
set cmd="(el,args...){"_cmd_"}"
set key = ""
for {
set el = ..collection.GetNext(.key)
quit:key=""
xecute (cmd,el,.Args)
}
}else{
if func[":" {
set className=$piece(func,":",1)
set methodName=$piece(func,":",2)
quit:'##class(%Dictionary.MethodDefinition).IDKEYExists(className,methodName) $$$ERROR($$$MethodDoesNotExist,func)
quit:'$$$defMemberKeyGet(className,"m",methodName,23) $$$ERROR($$$GeneralError,$$$FormatText("Метод %1 не является методом класса %2",methodName,className))
set key = ""
for {
set el = ..collection.GetNext(.key)
quit:key=""
$$$ReturnOnError($classmethod(className,methodName,el,.Args))
}
}else{
set methodName=func
set key = ""
for {
set el = ..collection.GetNext(.key)
quit:key=""
set className=$classname(el)
return:'##class(%Dictionary.MethodDefinition).IDKEYExists(className,methodName) $$$ERROR($$$MethodDoesNotExist,className_":"_methodName)
return:$$$defMemberKeyGet(className,"m",methodName,23) $$$ERROR($$$GeneralError,$$$FormatText("Метод %1 не является методом экземпляра класса %2",methodName,className))
$$$ReturnOnError($method(el,methodName,.Args))
}
}
}
quit $$$OK
}
/// <example>d ##class(test.ForEach).Test()</example>
ClassMethod Test() [ Internal ]
{
set old=$system.Process.Undefined(2)
try{
;============================== КОЛЛЛЕКЦИЯ ПРОСТЫХ ТИПОВ ДАННЫХ =============================
set t=##class(test.ForEach).%New("2,3,5")
;s t=##class(test.ForEach).%New("2,3,5,asd")
;s t=##class(test.ForEach).%New(##class(test.ForEach).%New()) ;раскомментируйте, чтобы увидеть ошибку
if '$IsObject(t) $$$ThrowStatus(%objlasterror)
write !,"==========",!,"test.myclass:Dump",!!
$$$ThrowOnError(t.Do("test.myclass:Dump"))
; или $$$ThrowOnError(t.Do("test.myclass:Dump",.result))
write !,"==========",!,"test.myclass:Dump(.r=""result"",""p1"",""p2"")",!!
set r="result" $$$ThrowOnError(t.Do("test.myclass:Dump",.r,"p1","p2"))
write !,"==========",!,"test.myclass:Sum(.r)",!!
$$$ThrowOnError(t.Do("test.myclass:Sum",.r)) write "Результат = ",r,!
;$$$ThrowOnError(t.Do("test.myclass:Sum",.r,5)) ;раскомментируйте, чтобы увидеть ошибку
write !,"==========",!,"+10",!! set r=10
$$$ThrowOnError(t.Do(,.r)) write "Результат = ",r,!
write !,"==========",!,"+",!! kill r
$$$ThrowOnError(t.Do(,.r)) write "Результат = ",r,!
write !,"==========",!,"*",!! set r=1
$$$ThrowOnError(t.Do("*",.r)) write "Результат = ",r,!
write !,"==========",!,"_ + разделитель=""^""",!! kill r
$$$ThrowOnError(t.Do("_",.r,"^")) write "Результат = ",r,!
write !,"==========",!,"min (входной аргумент не учитывается)",!!
set r="asd" $$$ThrowOnError(t.Do("min",.r)) write "Результат = ",r,!
write !,"==========",!,"max (входной аргумент не учитывается)",!!
set r="asd" $$$ThrowOnError(t.Do("max",.r)) write "Результат = ",r,!
write !,"==========",!,"avg (входной аргумент не учитывается)",!!
set r="asd" $$$ThrowOnError(t.Do("avg",.r)) write "Результат = ",r,!
write !,"==========",!,"s args(1,1)=args(1,1)+el",!!
kill r $$$ThrowOnError(t.Do($listbuild("s args(1,1)=args(1,1)+el"),.r)) write r,!
write !,"==========",!,"d sub^prog(el,args...) [.r=""r"",""p1"",""p2""]",!!
set r="r" $$$ThrowOnError(t.Do($listbuild("d sub^prog(el,args...)"),.r,"p1","p2"))
;============================== КОЛЛЛЕКЦИЯ СЛОЖНЫХ ТИПОВ ДАННЫХ =============================
set list=##class(%ListOfObjects).%New()
for i="f1","f2","f3" do list.Insert(##class(test.myclass).%New(i))
;f i="f1","f2","f3",7 d list.Insert(##class(test.myclass).%New(i))
set t=##class(test.ForEach).%New(list)
if '$IsObject(t) $$$ThrowStatus(%objlasterror)
write !,"++++++++++",!,"test.myclass:Dump",!!
$$$ThrowOnError(t.Do("test.myclass:Dump"))
write !,"++++++++++",!,"PrintLn",!!
$$$ThrowOnError(t.Do("PrintLn"))
write !,"++++++++++",!,"PrintLn(,""Элемент = "")",!!
$$$ThrowOnError(t.Do("PrintLn",,"Элемент = "))
write !,"++++++++++",!,"Concat(.r)",!! kill r
$$$ThrowOnError(t.Do("Concat",.r)) write "Результат = ",r,!
;$$$ThrowOnError(t.Do("Concat",.r,"f3")) w "Результат = ",r,! ;раскомментируйте, чтобы увидеть ошибку
write !,"++++++++++",!,"SetField(,""blablabla"") + PrintLn(,""Элемент = "")",!!
$$$ThrowOnError(t.Do("SetField",,"blablabla")) $$$ThrowOnError(t.Do("PrintLn",,"Элемент = "))
write !,"++++++++++",!,"d el.PrintLn(.args)",!!
$$$ThrowOnError(t.Do($listbuild("d el.PrintLn(.args)")))
write !,"++++++++++",!,"w ""field="",el.field,!",!!
$$$ThrowOnError(t.Do($listbuild("w ""field="",el.field,!")))
}catch(ex){
#dim ex As %Exception.AbstractException
write ex.DisplayString()
}
do $system.Process.Undefined(old)
}
}
Class test.myclass Extends %RegisteredObject
{
/// Строковое поле.
Property field;
/// Инициализация свойства <property>field</property>.
Method %OnNew(field) As %Status [ Internal, Private, ServerOnly = 1 ]
{
set ..field=field
quit $$$OK
}
/// Заполнение <property>field</property> первым <u>входным</u> аргументом.
Method SetField(Args...) As %Status
{
set ..field=Args(1,2)
quit $$$OK
}
/// Вывод <property>field</property> и первого <u>входного</u> аргумента.
Method PrintLn(Args...) As %Status
{
write Args(1,2),$$$quote(..field),!
quit $$$OK
}
/// Конкатенация <property>field</property> с разделителем (<span style="color:green;">метод <b>экземпляра</b> класса</span>).
/// <br>Если первый входной аргумент совпадает с <var>field</var>, генерируем ошибку (<span style="color:red;">для демонстрационных целей!</span>)
Method Concat(Args...) As %Status
{
set Args(1,1)=Args(1,1)_Args(1,2)_..field
quit $select(..field=Args(1,2):$$$ERROR($$$GeneralError,$$$FormatText("Возникла ошибка на элементе: %1",..field)),1:$$$OK)
}
/// Сумма <var>elem</var> (<span style="color:green;">метод класса</span>).
/// <br>Если первый <u>входной</u> аргумент совпадает с <var>elem</var> (он же <property>field</property>), генерируем ошибку (<span style="color:red;">для демонстрационных целей!</span>)
ClassMethod Sum(elem, Args...) As %Status
{
set Args(1,1)=Args(1,1)+elem
quit $select(elem=Args(1,2):$$$ERROR($$$GeneralError,$$$FormatText("Возникла ошибка на элементе: %1",elem)),1:$$$OK)
}
/// Вывод всех аргументов.
/// <br><br> <var>elem</var> = элемент коллекции
/// <br> <var>Args</var>(1) = кол-во переданных аргументов кроме первого, т.е. <var>elem</var>
/// <br> <var>Args</var>(1,1) = аргумент 1 (<span style="color:red;">у нас это входной/выходной аргумент</span>)
/// <br> <var>Args</var>(1,2) = аргумент 2
/// <br> ...
/// <br> <var>Args</var>(1,n) = аргумент n
ClassMethod Dump(elem, Args...) As %Status
{
set params=""
for i=2:1:Args(1) set params=params_$listbuild(Args(1,i))
if '$IsObject(elem) {
set el=elem
}elseif elem.%Extends("test.myclass"){
set el=elem.field
}else{
set el=elem.%ClassName($$$YES)
}
write "Элемент = ",$$$quote(el),", Выходной аргумент = ",$$$quote(Args(1,1)),", Дополнительные аргументы = ",$$$quote($listtostring(params)),!
quit $$$OK
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment