3

キーを検索して、キーScripting.Dictionary(およびそのアイテム)を辞書に1回だけ追加するようにしています。

If MyDict.Exists (Key) Then ' first internal key-value lookup
  Set Entry=MyDict.Item (Key)  ' Second internal key->value lookup
else
  Set Entry=New ItemType
  MyDict.Add Key,Entry
End If
' Now I work on Entry...

Exists辞書でキーを検索し、Item ()それも実行します。したがって、1つの論理ルックアップ操作に対して2つのキールックアップを取得します。もっと良い方法はありませんか?

Itemプロパティのdoxは言う

「既存のアイテムを返そうとしたときにキーが見つからない場合、新しいキーが作成され、対応するアイテムは空のままになります。」(MSDN

これは真実です。つまり、存在しないキーを検索すると、このキーが辞書の一部になり、おそらく関連するアイテムが空になります。しかし、それは何のために良いのでしょうか?これを使用して、1つのルックアップ操作にまとめるにはどうすればよいですか?Item ()プロパティ呼び出し中にキーを作成した後、空のアイテムを設定するにはどうすればよいですか?

4

2 に答える 2

2

このコードと出力:

>> Set d = CreateObject("Scripting.Dictionary")
>> WScript.Echo 0, d.Count
>> If d.Exists("soon to come") Then : WScript.Echo 1, d.Count : End If
>> WScript.Echo 2, d.Count
>> d("soon to come") = d("soon to come") + 1
>> WScript.Echo 3, d.Count, d("soon to come")
>>
0 0
2 0
3 1 1

ショー:

  1. .Existsを使用して存在しないキーを検索しても、キーはディクショナリに追加されませ(.Countは#2でまだ0です)
  2. サンプルコードの割り当ての右側にあるように、.Itemまたは()を介して存在しないキーにアクセスすると、キーと空のペアが辞書に追加されます。一部のタスク(頻度カウントなど)では、これは「機能」します。これは、空が追加で0として、または文字列連結で「」として扱われるためです。この小規模な自動活性化はオブジェクトには使用できません(Emptyをプログラマーが考えているオブジェクトにマップする適切な方法はなく、PythonやVBScriptのRubyのようなデフォルトの魔法もありません)
  3. 名前付きオブジェクトのディクショナリを維持する必要があり、名前とそのオブジェクトに同時にアクセスできる場合は、次のように記述できます。Set d(name) = object-d(name)は、必要に応じてキースロット「name」を作成し、Set割り当てによってオブジェクトが配置されます。対応する値に変換します(Emptyまたは'old'オブジェクト('pointer')を上書きします)。

あなたが本当に達成したいことについていくつかの詳細を追加するならば、私はこの答えに喜んで追加します。

追加した:

重複するキーのリストで(論理的に)作業し、その場で新しいオブジェクトをディクショナリに追加する必要がある場合、存在(1.0)を確認して(2.0)を割り当てる必要があるため、二重ルックアップを回避することはできません。 (おそらく新しく作成され割り当てられた(1.5))オブジェクトを作業変数に追加します(サンプルコードの/ m:aまたは/ m:bを参照)。値を提供するステートメントを持つ他の言語では、次のようなことが可能になる場合があります

if ! (oBJ = dicX( key )) {
   oBJ = dicX( key ) = new ItemType() 
}
oBJ.doSomething()

そしてVBScriptのセットなしでvs.

oBJ = dicX( key )
If IsEmpty( oBJ ) Then
   dicX( key ) = New ItemType
   oBJ = dicX( key ) 
End If

新しい要素に対してのみ追加の作業を行いますが、それはすべて夢のようなものです。

それらの二重検索が本当に重要である場合(私は疑っています-あなたは議論や証拠を与えることができますか?)、あなたのプログラムの全体的なデザインは重要です。例:作業リストを一意に調整できる場合は、すべてが単純になります(私のサンプルの/ m:cを参照)。確かに、そのような変更があなたの特定のタスクに可能かどうか、私はまだわかりません。

実験するコード:

Dim dicX  : Set dicX = CreateObject( "Scripting.Dictionary" )
Dim aKeys : aKeys    = Split( "1 2 3 4 4 3 2 1 5" )
Dim sMode : sMode    = "a"
Dim oWAN  : Set OWAN = WScript.Arguments.Named
If oWAN.Exists( "m" ) Then sMode = oWAN( "m" )
Dim sKey, oBJ
Select Case sMode
  Case "a"
    For Each sKey In aKeys
      If Not dicX.Exists( sKey ) Then 
         Set dicX( sKey ) = New cItemType.init( sKey )
      End If
      Set oBJ = dicX( sKey )
      WScript.Echo oBJ.m_sInfo
    Next    
  Case "b"  
    For Each sKey In aKeys
      If IsEmpty( dicX( sKey ) ) Then 
         Set dicX( sKey ) = New cItemType.init( sKey )
      End If   
      Set oBJ = dicX( sKey )
      WScript.Echo oBJ.m_sInfo
    Next    
  Case "c"  
    aKeys = uniqueList( aKeys )
    For Each sKey In aKeys
      Set dicX( sKey ) = New cItemType.init( sKey )
      Set oBJ = dicX( sKey )
      WScript.Echo oBJ.m_sInfo
    Next    
  Case Else
    WScript.Echo "Unknown /m:" & sMode & ", pick one of a, b, c."
End Select
WScript.Echo "----------"
For Each sKey In dicX.Keys
    WScript.Echo dicX( sKey ).m_sInfo
Next

Dim g_ITCnt : g_ITCnt = 0
Class cItemType
  Public m_sInfo
  Public Function init( sKey )
    Set init = Me
    g_ITCnt  = g_ITCnt + 1
    m_sInfo  = "Obj for " & sKey & " (" & g_ITCnt & ")"
  End Function   
End Class ' cItemType

Function uniqueList( aX )
  Dim dicU : Set dicU = CreateObject( "Scripting.Dictionary" )
  Dim vX
  For Each vX in aX
      dicU( vX ) = Empty
  Next    
  uniqueList = dicU.Keys
End Function

サンプル出力:

/m:a
Obj for 1 (1)
Obj for 2 (2)
Obj for 3 (3)
Obj for 4 (4)
Obj for 4 (4)
Obj for 3 (3)
Obj for 2 (2)
Obj for 1 (1)
Obj for 5 (5)
----------
Obj for 1 (1)
Obj for 2 (2)
Obj for 3 (3)
Obj for 4 (4)
Obj for 5 (5)
==================================================
xpl.vbs: Erfolgreich beendet. (0) [0.07031 secs]

/m:c
Obj for 1 (1)
Obj for 2 (2)
Obj for 3 (3)
Obj for 4 (4)
Obj for 5 (5)
----------
Obj for 1 (1)
Obj for 2 (2)
Obj for 3 (3)
Obj for 4 (4)
Obj for 5 (5)
================================================
xpl.vbs: Erfolgreich beendet. (0) [0.03906 secs]

タイミングの違いは、おそらく/ m:cモードの出力の低下が原因ですが、必要以上に頻繁に何かを行わないことの重要性を強調しています。

于 2011-08-02T12:15:46.453 に答える
1

私にとって重要な問題は、VBScriptがオブジェクトでの使用Setを強制し、オブジェクトでEmptyはないという事実です。これを回避するために過去に使用したトリックの1つは、Array関数を使用して値の一時的なプレースホルダーを作成することです。次に、配列をチェックして、値がオブジェクトであるかどうかを確認できます。あなたの例に適用:

tempArr = Array(dict.Item(key))
If IsEmpty(tempArr(0)) Then
    ' new entry
    Set entry = New MyClass
    ' can't use Add because the key has already been implicitly created
    Set dict.Item(key) = entry
Else
    ' existing entry
    Set entry = tempArr(0)
End If

ただし、この場合、二重ルックアップを削除していませんが、「既存のエントリ」の場合から「新しいエントリ」の場合に移動しました。

于 2011-08-03T04:57:38.397 に答える