求更好的字典初始化

Seeking better dictionary initialization

我最近在这里用 PowerShell 字典回答了一个问题,该字典使用 "ContainsKey" 来决定是定义键值还是在必要时添加键值。我经常这样做 - 最近通常在 C#、Python、R 或 PowerShell 中,我厌倦了它。

是否有一种语言——甚至是一个库——可以在一行中执行以下 PowerShell 代码块?

  if ($sums.ContainsKey($skey))
  {
        $sums[$skey] += $sval 
  }
  else
  {
        $sums[$skey] = $sval 
  }
.NET 中的

ConcurrentDictionary 将允许您这样做,是的:

sums.AddOrUpdate(key, value, (k, v) => v + value);

您应该也可以在 PowerShell 中使用它(显然需要更改语法)。

或者,如果您想在 .NET 中为纯 Dictionary 执行此操作,您可以添加扩展方法:

public static void AddOrUpdate<TKey, TValue>(
    this Dictionary<TKey, TValue> dictionary,
    TKey key,
    TValue addValue,
    Func<TKey, TValue, TValue> updateValueFactory)
{
    TValue existing;
    if (dictionary.TryGetValue(key, out existing))
    {
        dictionary[key] = updateValueFactory(key, existing);
    }
    else
    {
        dictionary[key] = addValue;
    }
}

这被写入与ConcurrentDictionary方法具有相同的有效签名;如果您只需要 Func<TValue, TValue> 作为更新工厂,您可以相应地更改它。

我想您可以在 Python 中使用辅助方法采用相同的方法 - 我对 Python 的了解还不够多,无法说明您是否可以做类似扩展方法的事情。

Python(我在其中使用 Python 3),collections.defaultdict 的构造函数采用工厂方法,用于为缺少的键创建默认值。 int() returns 0, 便于计数

>>> import collections
>>> sums = collections.defaultdict(int)
>>> sums['a'] += 23
>>> sums['a'] += 12
>>> sums
defaultdict(<class 'int'>, {'a': 35})

或者如果你有一个可迭代的东西,你可以使用 collections.Counter:

>>> sums = collections.Counter(['a', 'b']) 
>>> sums['b'] += 2
>>> sums.update(['c', 'd', 'e'])
>>> sums
Counter({'b': 3, 'c': 1, 'a': 1, 'e': 1, 'd': 1})

假设您有一个要求输入的函数:

>>> def input_skey():
...    return input("Give a next skey, empty line ends> ")
...
>>> sums = collections.Counter(iter(input_skey, ''))
Give a next skey, empty line ends> foo
Give a next skey, empty line ends> bar
Give a next skey, empty line ends> baz
Give a next skey, empty line ends> baz
Give a next skey, empty line ends> bar
Give a next skey, empty line ends> foo
Give a next skey, empty line ends> baz
Give a next skey, empty line ends> 42
Give a next skey, empty line ends> 
>>> sums
Counter({'baz': 3, 'bar': 2, 'foo': 2, '42': 1})

iter(function, sentinel) 使重复调用 function 的迭代器直到函数 returns 的值等于 sentinel)。

在 Perl 中(你确实问过 "a language"),你只需添加它:

my %sums;

$sums{$skey} += $sval;

如果密钥不存在,它将创建一个值为 undef 的密钥,该值在数值上等于零,然后将 $sval 添加到该密钥中。如果密钥确实存在,则操作如您所料。

您可以在 Powershell 中一行完成:

$sums[$skey] += $sval

如果 $skey 不作为键存在于 $sums 中,将添加它,值为 $sval。

如果存在,则根据现有值的类型使用+=运算符的规则更新当前值。如果它是一个数字,它将进行数学加法,如果它是一个字符串,它将连接起来,如果它是一个数组或集合,它将作为一个新元素添加。