Scripting.Dictionary Lookup-add-if-not-present только с одним поиском ключа?
Я ищу ключи в Scripting.Dictionary
чтобы добавить их (и их элементы) в словарь только один раз:
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 ()
делает это тоже. Таким образом, я получаю два ключевых поиска для одной логической операции поиска. Разве нет лучшего способа?
Dox для Item
собственность сказать
"Если ключ не найден при попытке вернуть существующий элемент, создается новый ключ и соответствующий ему элемент остается пустым". ( MSDN)
Это действительно так, то есть поиск несуществующего ключа, очевидно, делает этот ключ частью словаря, возможно, с соответствующим item = empty. Но для чего это нужно? Как я мог использовать это, чтобы свести это к одной операции поиска? Как я могу установить пустой элемент после создания ключа во время Item ()
вызов недвижимости?
2 ответа
Ключевой проблемой для меня является тот факт, что VBScript заставляет нас использовать Set
с объектами, и Empty
это не объект. Один прием, который я использовал в прошлом, чтобы обойти это, это использовать 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
В этом случае, однако, вы не исключили двойной поиск, а переместили его из дела "существующая запись" в дело "новая запись".
Этот код и вывод:
>> 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
показывает:
- Поиск несуществующего ключа с помощью.Exists не добавляет ключ в словарь (.Count по-прежнему равен 0 в #2)
- Доступ к несуществующему ключу через.Item или () - как в правой части назначения в моем примере кода - добавляет пару ключ / пусто в словарь; для некоторой задачи (например, подсчет частоты) это "работает", потому что Empty обрабатывается как 0 дополнительно или "" в конкатенации строк. Эту мелкомасштабную автовивификацию нельзя использовать для объектов (нет приличного способа сопоставления Empty с любым объектом, который придумывает программист, и никакой магии по умолчанию, как в Python или Ruby в VBScript)
- Если вам нужно вести словарь именованных объектов и одновременно иметь доступ к имени и его объекту, вы можете просто написать
Set d(name) = object
- d(name) создаст ключевой слот "name", если необходимо, а назначение Set поместит объект в соответствующее значение (перезаписывает пустой или "старый" объект ("указатель")).
Если вы добавите некоторые подробности о том, чего вы действительно хотите достичь, я хочу добавить к этому ответу.
Добавлено:
Если вы (логически) работаете со списком ключей с дубликатами и должны добавлять новые объекты в словарь на лету, вы не можете избежать двойного поиска, потому что вам нужно проверить наличие (1.0) и назначить (2.0) (возможно, недавно созданный и назначенный (1.5)) объект вашей рабочей переменной (см. /m:a или /m:b в моем примере кода). Другие языки с операторами, которые предоставляют значение, могут разрешать что-то вроде
if ! (oBJ = dicX( key )) {
oBJ = dicX( key ) = new ItemType()
}
oBJ.doSomething()
и без набора VBScript против пусть мерзость что-то вроде
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, но это подчеркивает важность того, чтобы не делать что-то чаще, чем необходимо.