Реализация интервально-ассоциативного массива в СУБД Caché
Class test.intervalmap Extends %RegisteredObject [ Final ]{Parameter None [ Final, Internal ] = -1; Property bounds As %List [ Internal, Private, ReadOnly, ServerOnly = 1, Transient ]; Property items As %List [ Internal, Private, ReadOnly, ServerOnly = 1, Transient ]; Property upperItem [ InitialExpression = {…#None}, Internal, Private, ReadOnly, ServerOnly = 1, Transient ]; Property % [ MultiDimensional, ReadOnly, ServerOnly = 1, Transient ]; ClassMethod Slice (list As %List, start, end, value As %List) As %List [ Internal, Private ]{ if start=end { if start=» { set list=» }elseif start=1 { set list=value_list }else{ if start>$listlength (list) { set list=list_value }else{ set: start set $list (list, start, start)=value_$listbuild ($list (list, start)) } } }else{ if end=» { set: start>$listlength (list) start=$listlength (list) set $list (list, start,*+1)=value }else{ set start=$get (start,1) if end=1 { set list=…Slice (list, start, end, value) }else{ set $list (list, start, end-1)=value } } } quit list}Method Reset (){ kill i%% set (i%bounds, i%items)=», i%upperItem=…#None}Method %(start = {$get (start,…#None)}, stop = {$get (stop,…#None)}, value = {$get (value,…#None)}) As %Status{ quit:(start>=stop)&((start'=…#None)&(stop'=…#None)) $$$OK set startPoint=$select (start=…#None:0,1:…bisectLeft (start)) set endPoint=$select (stop=…#None:0,1:…bisectLeft (stop)) if startPoint>=1 { set:(startPoint set:(endPoint >= 1)&&(endPoint if endPoint>=1 { set i%bounds=…Slice (i%bounds, startPoint, endPoint,$listbuild (start, stop)) set i%items=…Slice (i%items, startPoint, endPoint,$select (startPoint }else{ set $list (i%bounds, startPoint,*+1) = $listbuild (start) set $list (i%items, startPoint,*+1)=$select (startPoint set i%upperItem = value } }else{ if endPoint>=1 { set i%bounds=…Slice (i%bounds,1, endPoint,$listbuild (stop)) set i%items=…Slice (i%items,1, endPoint,$listbuild (value)) }else{ set (i%bounds, i%items) = » set i%upperItem = value } } set i=1 while (i { if $list (…items, i)=$list (…items, i+1) { set $list (i%items, i, i)=» set $list (i%bounds, i, i)=» }else { set i=i+1 } } set:($listlength (…items)=1)&&($list (i%items,1)=i%upperItem) (i%items, i%bounds)=» do …repr () quit $$$OK}Method %Get (x) As %String [ ServerOnly = 1 ]{ set index=…bisectRight (x) set r=$select (index quit $select (r=…#None:»,1: r)}Method bisectLeft (x) As %String [ Internal, Private, ServerOnly = 1 ]{ set lo = 1 set hi = $listlength (i%bounds)+1 while (lo set mid = (lo+hi)\2 if $list (i%bounds, mid) set lo = mid+1 } else { set hi = mid } } quit lo}Method bisectRight (x) As %String [ Internal, Private, ServerOnly = 1 ]{ set lo = 1 set hi = $listlength (i%bounds)+1 while (lo set mid = (lo+hi)\2 if x set hi = mid } else { set lo = mid+1 } } quit lo}Method repr () [ Internal, Private, ServerOnly = 1 ]{ kill i%% set previousBound=…#None for i=1:1:$listlength (…bounds) { set b=$list (…bounds, i) set v=$list (…items, i) set: v'=…#None i%%(previousBound, b)=v set previousBound=b } set:…upperItem'=…#None i%%(previousBound,…#None)=…upperItem}Method Shrink (){ set i=1 while (i { if $list (…items, i)'=…#None,$list (…items, i+1)'=…#None { set $list (i%items, i, i)=» set $list (i%bounds, i, i)=» }else { set i=i+1 } } do …repr ()}Method Display () As %String{ #define IsNone (%s) $s (%s=…#None: «None»,1:%s) set key=$query (i%%,1, v), s=» while (key'=») { set s=s_$listbuild ($$$FormatText (»[%1, %2] => %3»,$$$IsNone ($qsubscript (key,1)),$$$IsNone ($qsubscript (key,2)), v)) set key = $query (@key,1, v) } quit $listtostring (s,», »)}/// d ##class (test.intervalmap).Test1()ClassMethod Test1() [ Internal, ServerOnly = 1 ]{ new % set old=$system.Process.Undefined (2) try{ set i=…%New () do i.%(»08:00»,»12:00», «Иванов») do i.%(»12:00»,»16:00», «Петров») do i.%(»15:00»,»16:00») do i.%(»12:00»,»16:00») do i.%(»11:00»,»15:00», «Сидоров») do i.%(»15:00»,»17:00», «Сидоров») do i.%(»17:00»,»20:00», «Петров») do i.%(»21:00»,»23:00», «Сидоров») write i.Display (),! write »[13:51] = », i.%Get (»13:51»),! ; k % m %=i.% zw % do i.Shrink () write i.Display (),! }catch (ex){ #dim ex As %Exception.AbstractException write «Error = », ex.DisplayString (),! } do $system.Process.Undefined (old)}/// d ##class (test.intervalmap).Test2()ClassMethod Test2() [ Internal, ServerOnly = 1 ]{ #define Assert (%i,%s) if %i.Display ()'=%s {$$$ThrowStatus ($$$ERROR ($$$GeneralError,%s))} else {w %i.Display (),!} #define AssertGet (%i,%t,%s) if %i.%Get (%t)'=%s {$$$ThrowStatus ($$$ERROR ($$$GeneralError,%s))} else {w »(%t) = »,%i.%Get (%t),!} set old=$system.Process.Undefined (2) try{ set i=…%New () do i.%(0,5,»0–5») do i.%(8,12,»8–12») $$$AssertGet (i,2,»0–5») $$$AssertGet (i,10,»8–12») $$$AssertGet (i,-1,») $$$AssertGet (i,17,») do i.%(4,9,»4–9») $$$Assert (i,»[0, 4] => 0–5, [4, 9] => 4–9, [9, 12] => 8–12») do i.%(,0, «less than 0») $$$AssertGet (i,-5, «less than 0») $$$AssertGet (i,0,»0–5») $$$Assert (i,»[None, 0] => less than 0, [0, 4] => 0–5, [4, 9] => 4–9, [9, 12] => 8–12») do i.%(21,, «more than twenty») $$$AssertGet (i,42, «more than twenty») do i.%(10.5,15.5,»10.5–15.5») $$$AssertGet (i,11.5,»10.5–15.5») $$$AssertGet (i,0.5,»0–5») $$$Assert (i,»[None, 0] => less than 0, [0, 4] => 0–5, [4, 9] => 4–9, [9, 10.5] => 8–12, [10.5, 15.5] => 10.5–15.5, [21, None] => more than twenty») do i.Reset () do i.%(0,2,1) do i.%(2,8,2) do i.%(4,,3) do i.%(5,6,4) $$$Assert (i,»[0, 2] => 1, [2, 4] => 2, [4, 5] => 3, [5, 6] => 4, [6, None] => 3») }catch (ex){ #dim ex As %Exception.AbstractException write «Error = », ex.DisplayString (),! } do $system.Process.Undefined (old)}/// d ##class (test.intervalmap).Test3()ClassMethod Test3() [ Internal, ServerOnly = 1 ]{ #define Assert (%i,%s) if %i.Display ()'=%s $$$ThrowStatus ($$$ERROR ($$$GeneralError,%s)) #define AssertGet (%i,%t,%s) if %i.%Get (%t)'=%s $$$ThrowStatus ($$$ERROR ($$$GeneralError,%s)) set old=$system.Process.Undefined (2) try{ set i=…%New () do i.%(9,,»!») $$$Assert (i,»[9, None] => !») do i.%(,5, «Hello») do i.%(6,7, «World») $$$Assert (i,»[None, 5] => Hello, [6, 7] => World, [9, None] => !») do i.%(8,10,»(Test)») $$$Assert (i,»[None, 5] => Hello, [6, 7] => World, [8, 10] => (Test), [10, None] => !») do i.%(,3, «My,») $$$Assert (i,»[None, 3] => My, [3, 5] => Hello, [6, 7] => World, [8, 10] => (Test), [10, None] => !») do i.%(5.5,6, «Cruel») $$$Assert (i,»[None, 3] => My, [3, 5] => Hello, [5.5, 6] => Cruel, [6, 7] => World, [8, 10] => (Test), [10, None] => !») do i.%(6,6.5, «And Harsh») $$$Assert (i,»[None, 3] => My, [3, 5] => Hello, [5.5, 6] => Cruel, [6, 6.5] => And Harsh, [6.5, 7] => World, [8, 10] => (Test), [10, None] => !») do i.%(5.9,6.6) $$$Assert (i,»[None, 3] => My, [3, 5] => Hello, [5.5, 5.9] => Cruel, [6.6, 7] => World, [8, 10] => (Test), [10, None] => !») write «Test 1 OK»,! do i.Reset () do i.%(,0, «A») do i.%(2,5, «B») do i.%(8,10, «C») do i.%(12,, «D») $$$Assert (i,»[None, 0] => A, [2, 5] => B, [8, 10] => C, [12, None] => D») do i.%(,, «K») $$$Assert (i,»[None, None] => K») $$$AssertGet (i,5, «K») do i.%(0,10, «L») do i.%(6,8, «M») do i.%(20,, «J») $$$AssertGet (i,-1, «K») $$$AssertGet (i,5, «L») $$$AssertGet (i,7, «M») $$$AssertGet (i,9, «L») $$$AssertGet (i,15, «K») write «Test 2 OK»,! do i.Reset () do i.%(,$zdateh (»24.10.2005»), «A») do i.%($zdateh (»11.11.2005»),$zdateh (»17.11.2005»), «B») do i.%($zdateh (»30.11.2005»),, «C») $$$AssertGet (i,$zdateh (»25.09.2005»), «A») $$$AssertGet (i,$zdateh (»23.10.2005»), «A») $$$AssertGet (i,$zdateh (»26.10.2005»),») $$$AssertGet (i,$zdateh (»09.11.2005»),») $$$AssertGet (i,$zdateh (»16.11.2005»), «B») $$$AssertGet (i,$zdateh (»23.11.2005»),») $$$AssertGet (i,$zdateh (»29.11.2005»),») $$$AssertGet (i,$zdateh (»30.11.2005»), «C») $$$AssertGet (i,$zdateh (»03.12.2005»), «C») write «Test 3 OK»,! }catch (ex){ #dim ex As %Exception.AbstractException write «Error = », ex.DisplayString (),! } do $system.Process.Undefined (old)}}