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