CachéDBMSでの区間連想配列の実装

この投稿は、Habrの記事「間隔連想配列」に基づいて書かれています。



元の実装はpythonのスライスのスライスに基づいているため、記事「 スライスについて知りたいことすべて」を読むと便利です。 そして、もちろん、ちょっとした理論: 間隔のツリー(セグメント) 。

それでは、Cachéではスライスはどのように表示されますか?



一般的に、すべては(ほぼ)pythonのようです。



割り当てが簡単:



s i = ##クラス ( test.intervalmap )。 %新規 ()

> d i 。 % ( "08:00" 、 "12:00" 、 "Ivanov" )

> d i 。 % ( "12:00" 、 "16:00" 、 "ペトロフ" )

13:51に誰が勤務していたかを知る方法は?



w i 。 %Get ( "13:51" ) 、!

ペトロフ

アイテムの完全なリストを参照するのは簡単です(「グローバルに」考えることに慣れている人向け):



> k % m % = i %zw %

%( "08:00"、 "12:00")= "イワノフ"

%( "12:00"、 "16:00")= "ペトロフ"

...または1行で:



> w i 。 ()を 表示し ます!

[08:00、12:00] =>イワノフ、[12:00、16:00] =>ペトロフ

部品の取り外し:



> d i 。 % ( "15:00" 、 "16:00" )

> w i 。 ()を 表示し ます!

[08:00、12:00] =>イワノフ、[12:00、15:00] =>ペトロフ

...など:



> d i 。 % ( "12:00" 、 "16:00" )

> w i 。 ()を 表示し ます!

[08:00、12:00] =>イワノフ

キーの重複は正しく処理する必要があります。



> d i 。 % ( "11:00" 、 "15:00" 、 "Sidorov" )

> w i 。 ()を 表示し ます!

[08:00、11:00] =>イワノフ、[11:00、15:00] =>シドロフ

同じ値を持つ隣接キーは、自動的に結合します。 たとえば、Sidorovを15から17まで勤務するように任命した場合、これが連続して2シフトであるとは考えられません。



> d i 。 % ( "15:00" 、 "17:00" 、 "Sidorov" )

> w i 。 ()を 表示し ます!

[08:00、11:00] =>イワノフ、[11:00、17:00] =>シドロフ

いくつかのエントリを追加します。



> d i 。 % ( "17:00" 、 "20:00" 、 "ペトロフ" )

> d i 。 % ( "21:00" 、 "23:00" 、 "Sidorov" )

> w i 。 ()を 表示し ます!

[08:00、11:00] =>イワノフ、[11:00、17:00] =>シドロフ、[17:00、20:00] =>ペトロフ、[21:00、23:00] =>シドロフ

多くの場合、いくつかの連続した要素の最後のみを残して、スケジュールをトリミングするタスクがあります。 たとえば、誰が就業日を閉じたかを調べる必要があります。



> d i 。 縮小 ()

> w i 。 ()を 表示し ます!

[08:00、20:00] =>ペトロフ、[21:00、23:00] =>シドロフ

同じ方法を使用して、スケジュールが営業日全体をカバーしているかどうかを確認できます。



追加

隣接するキーを接着するためのソースコードの小さなエラーが考慮されました。



Python:

>>> timetable = intervalmap() >>> timetable[:] = '' >>> timetable['11:00':'13:00'] = '' >>> print timetable {[None, '13:00'] => '', ['13:00', None] => ''}
      
      





COS:



> s i = ##クラス ( test.intervalmap )。 %新規 ()

> d i 。 % (,, "Ivanov" )

> d i 。 % ( "11:00" 、 "13:00" 、 "イワノフ" )

> w i 。 ()を 表示し ます!

[なし、なし] =>イワノフ

ここでは、ソースコードのようにペアで同じキーを使用することはできませんが、自分でキーをオンに戻すことができます。

もちろん、数値/文字列値はすべてキーとして機能できます。 それらがすべて同じ配列(オブジェクト)内で同じ型であることを確認するために必要な唯一のものです。

例:



> s i = .. %New ()

> d i 。 % (9 ,, "!" )

> d i 。 % (、5、 "Hello" )

> d i 。 % (6.7、 「ワールド」 )

> w i 。 ()を 表示し ます!

[なし、5] =>こんにちは、[6、7] =>ワールド、[9、なし] =>!



> d i 。 リセット ()

> d i 。 % (、 $ zdh ( "10.24.2005" )、 "A" )

> d i 。 % ( $ zdh ( "11/11/2005" )、 $ zdh ( " 11/17/2005 " )、 "B" )

> d i 。 % ( $ zdh ( " 11/30/2005 " ),, "C" )

> w i 。 ()を 表示し ます!

[なし、60197] => A、[60215、60221] => B、[60234、なし] => C

最後に、別の例はより複雑です。



> d i 。 リセット ()

> d i 。 % (,, $ c (8734))

> d i 。 % (10.11、 「イワノフ」 )

> d i 。 % (12.13、 「イワノフ」 )

> d i 。 % (14.16、 ペトロフ )

> d i 。 % (11.15、 イワノフ )

> d i 。 % (8.12、 シドロフ )

> d i 。 % (20.21)

> d i 。 % (22 ,, Sidorov )

> w i 。 ()を 表示し ます!

ネタバレの結果
[なし、8] =>∞、[8、12] =>シドロフ、[12、15] =>イワノフ、[15、16] =>ペトロフ、[16、20] =>∞、[21、22] =>∞、[22、なし] =>シドロフ


ソースコードにはさらに多くの例があります。



クラスコード


クラスtest.intervalmap Extends%RegisteredObject [ Final ]

{

パラメータ なし[ 最終 、 内部 ] = -1 ;

プロパティ 境界 As%List [ Internal 、 Private 、 ReadOnly 、 ServerOnly = 1、 Transient ];

プロパティ 項目 As%List [ Internal 、 Private 、 ReadOnly 、 ServerOnly = 1、 Transient ];

プロパティ upperItem [ InitialExpression = {.. #None }、 Internal 、 Private 、 ReadOnly 、 ServerOnly = 1、 Transient ];

プロパティ %[ MultiDimensional 、 ReadOnly 、 ServerOnly = 1、 Transient ];

ClassMethod Slice( list As%List 、 start 、 end 、 value As%List ) As%List [ 内部 、 プライベート ]

{

start = end {の 場合

if start = "" {

セット リスト = ""

} elseif start = 1 {

set list = value _ list

} else {

start > $ listlength ( list ) {

set list = list _ value

} else {

set : start < $ listlength ( list ) start = start +1

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)

end = 1の 場合 {

set list = .. Slice ( list 、 start 、 end 、 value )

} else {

set $ list ( list 、 start 、 end -1)= value

}

}

}

リストを 終了

}

メソッドの リセット()

{

私を 殺す %%

set (i%bounds、i%items)= "" 、i%upperItem = .. #None

}

メソッド %( start = { $ get ( start 、.. #None )}、 stop = { $ get ( stop 、.. #None )}、 value = { $ get ( value 、.. #None )}) As%ステータス

{

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 ))



startPoint > = 1の 場合 {

set :( startPoint <= $ listlength (.. bounds ))&&( $ list (.. bounds 、 startPoint )< start ) startPoint = startPoint + 1

set :( endPoint > = 1)&&( endPoint <= $ listlength (.. bounds ))&&( $ list (.. bounds 、 endPoint )<= stop ) endPoint = endPoint + 1



endPoint > = 1の 場合 {

set i%bounds = .. Slice (i%bounds、 startPoint 、 endPoint 、 $ listbuild ( start 、 stop ))

set i%items = .. Slice (i%items、 startPoint 、 endPoint 、 $ select ( startPoint <= $ listlength (.. items ): $ listbuild ( $ list (.. items 、 startPoint )、 value )、1: $ listbuild (.. upperItem 、 value ))))

} else {

set $ list (i%bounds、 startPoint 、* + 1)= $ listbuild ( start )

set $ list (i%items、 startPoint 、* + 1)= $ select ( startPoint <= $ listlength (.. items ): $ listbuild ( $ list (.. items 、 startPoint )、 value )、1: $ listbuild ( .. upperItem ))

i%upperItem = valueを 設定し ます

}

} else {

endPoint > = 1の 場合 {

set i%bounds = .. Slice (i%bounds、1、 endPoint 、 $ listbuild ( stop ))

set i%items = .. Slice (i%items、1、 endPoint 、 $ listbuild ( value ))

} else {

設定 (i%境界、i%アイテム)= ""

i%upperItem = valueを 設定し ます

}

}

i = 1に 設定

while ( i <=( $ listlength (.. items )-1))

{

if $ list (.. items 、 i )= $ list (.. items 、 i +1) {

set $ list (i%items、 i 、 i )= ""

set $ list (i%bounds、 i 、 i )= ""

} else {

i = i +1に 設定

}

}

set :( $ listlength (.. items )= 1)&&( $ list (i%items、1)= i%upperItem)(i%items、i%bounds)= ""



do .. repr ()



$$$を終了OK

}

メソッド %Get( x ) As%文字列 [ ServerOnly = 1]

{

セット インデックス = .. bisectRight ( x )

set r = $ select ( index <= $ listlength (i%items): $ list (i%items、 index )、1:i%upperItem)

quit $ select ( r = .. #None : "" 、1: r )

}

メソッド bisectLeft( x ) As%String [ Internal 、 Private 、 ServerOnly = 1]

{

lo = 1に 設定

set hi = $ listlength (i%bounds)+1

while ( lo < hi ) {

set mid =( lo + hi )\ 2

if $ list (i%bounds、 mid )< x {

lo = mid +1に 設定

} else {

hi = midに 設定

}

}

やめて

}

メソッド bisectRight( x ) As%String [ Internal 、 Private 、 ServerOnly = 1]

{

lo = 1に 設定

set hi = $ listlength (i%bounds)+1

while ( lo < hi ) {

set mid =( lo + hi )\ 2

if x < $ list (i%bounds、 mid ) {

hi = midに 設定

} else {

lo = mid +1に 設定

}

}

やめて

}

メソッド repr()[ Internal 、 Private 、 ServerOnly = 1]

{

私を 殺す %%

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

previousBound = bを 設定します

}

セット :.. upperItem '= .. #None i %%( previousBound 、.. #None )= .. upperItem

}

メソッドの 縮小()

{

i = 1に 設定

while ( i <=( $ listlength (.. items )-1))

{

if $ list (.. items 、 i ) '= .. #None 、 $ list (.. items 、 i +1)' = .. #None {

set $ list (i%items、 i 、 i )= ""

set $ list (i%bounds、 i 、 i )= ""

} else {

i = i +1に 設定

}

}

do .. repr ()

}

メソッド 表示() 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 、 "、" )

}

/// <example> d ## class(test.intervalmap).Test1()</ example>

ClassMethod Test1()[ Internal 、 ServerOnly = 1]

{

新しい %

set old = $ system .Process 。 未定義 (2)



{

set i = .. %New ()

します 。 % ( "08:00" 、 "12:00" 、 "Ivanov" )

します 。 % ( "12:00" 、 "16:00" 、 "ペトロフ" )

します 。 % ( "15:00" 、 "16:00" )

します 。 % ( "12:00" 、 "16:00" )

します 。 % ( "11:00" 、 "15:00" 、 "Sidorov" )

します 。 % ( "15:00" 、 "17:00" 、 "Sidorov" )

します 。 % ( "17:00" 、 "20:00" 、 "ペトロフ" )

します 。 % ( "21:00" 、 "23:00" 、 "Sidorov" )

私を 書き ます。 ()を 表示し ます!

「[13:51] = "」 と 書き ます。 %Get ( "13:51" ) 、!

; k%m%= i。%zw%

します 。 縮小 ()

私を 書き ます。 ()を 表示し ます!

} catch ( ex ) {

#dim ex As %Exception.AbstractException

"Error =" 、 exを 書き込み ます。 DisplayString () 、!

}

$ system .Processを実行し ます。 未定義 ( 古い )

}

/// <example> d ## class(test.intervalmap).Test2()</ example>

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 。 未定義 (2)



{



set i = .. %New ()

します 。 % (0.5、 "0-5" )

します 。 % (8.12、 "8-12" )

$$$ AssertGet ( i 、2、 "0-5" )

$$$ AssertGet ( i 、10、 "8-12" )

$$$ AssertGet ( i 、-1、 "" )

$$$ AssertGet ( i 、17、 "" )



します 。 % (4.9、 「4-9」 )

$$$アサート ( i 、 "[ 0、4 ] => 0-5、[4、9] => 4-9、[9、12] => 8-12" )

します 。 % (、0、 "0未満" )

$$$ AssertGet ( i 、-5、 「0未満」 )

$$$ AssertGet ( i 、0、 "0-5" )

$$$アサート ( i 、 "[なし、0] => 0未満、[0、4] => 0-5、[4、9] => 4-9、[9、12] => 8- 12 " )



します 。 % (21 ,, "20以上" )

$$$ AssertGet ( i 、42、 "20以上" )

します 。 % (10.5,15.5、 "10.5-15.5" )

$$$ AssertGet ( i 、11.5、 "10.5-15.5" )

$$$ AssertGet ( i 、0.5、 "0-5" )

$$$アサート ( i 、 "[None、0] => 0未満、[0、4] => 0-5、[4、9] => 4-9、[9、10.5] => 8- 12、[10.5、15.5] => 10.5-15.5、[21、なし] => 20を超える " )



します 。 リセット ()



します 。 % (0,2,1)

します 。 % (2,8,2)

します 。 % (4 ,, 3)

します 。 % (5,6,4)

$$$アサート ( i 、 "[0、2] => 1、[2、4] => 2、[4、5] => 3、[5、6] => 4、[6、None] = > 3 " )



} catch ( ex ) {

#dim ex As %Exception.AbstractException

"Error =" 、 exを 書き込み ます。 DisplayString () 、!

}

$ system .Processを実行し ます。 未定義 ( 古い )

}

/// <example> d ## class(test.intervalmap).Test3()</ example>

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 ( $$$エラー ( $$$ GeneralError 、 %s ))

set old = $ system .Process 。 未定義 (2)



{



set i = .. %New ()

します 。 % (9 ,, "!" )

$$$アサート ( i 、 "[9、None] =>!" )

します 。 % (、5、 "Hello" )

します 。 % (6.7、 「ワールド」 )

$$$アサート ( i 、 "[None、5] => Hello、[6、7] => World、[9、None] =>!" )

します 。 % (8,10、 "(テスト)" )

$$$アサート ( i 、 "[None、5] => Hello、[6、7] => World、[8、10] =>(Test)、[10、None] =>!" )

します 。 % (、3、 "マイ、" )

$$$アサート ( i 、 "[None、3] => My ,, [3、5] => Hello、[6、7] => World、[8、10] =>(Test)、[10、なし] =>! " )

する % (5.5.6、 「残酷」 )

$$$ Assert ( i 、 "[None、3] => My ,, [3、5] => Hello、[5.5、6] => Cruel、[6、7] => World、[8、10] =>(テスト)、[10、なし] =>! " )

します 。 % (6.6.5、 「そして厳しい」 )

$$$アサート ( i 、 "[None、3] => My ,, [3、5] => Hello、[5.5、6] => Cruel、[6、6.5] => And Harsh、[6.5、7 ] =>ワールド、[8、10] =>(テスト)、[10、なし] =>! " )

します 。 % (5.9,6.6)

$$$アサート ( i 、 "[None、3] => My ,, [3、5] => Hello、[5.5、5.9] => Cruel、[6.6、7] => World、[8、10] =>(テスト)、[10、なし] =>! " )

「テスト1 OK」 と 書いて ください!



します 。 リセット ()

します 。 % (、0、 "A" )

します 。 % (2.5、 「B」 )

します 。 % (8,10、 "C" )

します 。 % (12 ,, "D" )

$$$アサート ( i 、 "[なし、0] => A、[ 2、5 ] => B、[8、10] => C、[12、なし] => D" )

します 。 % (,, "K" )

$$$アサート ( i 、 "[なし、なし] => K" )

$$$ AssertGet ( i 、5、 "K" )

します 。 % (0.10、 "L" )

します 。 % (6.8、 「M」 )

します 。 % (20 ,, "J" )

$$$ AssertGet ( i 、-1、 "K" )

$$$ AssertGet ( i 、5、 "L" )

$$$ AssertGet ( i 、7、 "M" )

$$$ AssertGet ( i 、9、 "L" )

$$$ AssertGet ( i 、15、 "K" )

「テスト2 OK」 と 書いて ください!



します 。 リセット ()

します 。 % (、 $ zdateh ( "10.24.2005" )、 "A" )

します 。 % ( $ zdateh ( "11/11/2005" )、 $ zdateh ( " 11/17/2005 " )、 "B" )

する % ( $ zdateh ( " 11/30/2005 " ),, "C" )

$$$ AssertGet ( i 、 $ zdateh ( "09/25/2005" )、 "A" )

$$$ AssertGet ( i 、 $ zdateh ( " 10.23.2005 " )、 "A" )

$$$ AssertGet ( i 、 $ zdateh ( " 10.26.2005 " )、 "" )

$$$ AssertGet ( i 、 $ zdateh ( " 11/09/2005 " )、 "" )

$$$ AssertGet ( i 、 $ zdateh ( "11/16/2005" )、 "B" )

$$$ AssertGet ( i 、 $ zdateh ( " 11/23/2005 " )、 "" )

$$$ AssertGet ( i 、 $ zdateh ( "11/29/2005" )、 "" )

$$$ AssertGet ( i 、 $ zdateh ( " 11/30/2005 " )、 "C" )

$$$ AssertGet ( i 、 $ zdateh ( " 12/03/2005 " )、 "C" )

「テスト3 OK」 と 書いて ください!



} catch ( ex ) {

#dim ex As %Exception.AbstractException

"Error =" 、 exを 書き込み ます。 DisplayString () 、!

}

$ system .Processを実行し ます。 未定義 ( 古い )

}

}



または、test.intervalmapクラスをダウンロードします 。

コードはCaché2015.1バージョンでテストされましたが、以前のバージョン用にクラスを作り直すことは難しくありません。



All Articles