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バージョンでテストされましたが、以前のバージョン用にクラスを作り直すことは難しくありません。

Source: https://habr.com/ru/post/J256851/


All Articles