【问题标题】:Analogue of Python's defaultdict?类似于 Python 的 defaultdict?
【发布时间】:2013-03-15 09:29:40
【问题描述】:

是否有 Python 的 defaultdict 的 .NET 类似物?我发现编写短代码很有用,例如。计数频率:

>>> words = "to be or not to be".split()
>>> print words
['to', 'be', 'or', 'not', 'to', 'be']
>>> from collections import defaultdict
>>> frequencies = defaultdict(int)
>>> for word in words:
...     frequencies[word] += 1
... 
>>> print frequencies
defaultdict(<type 'int'>, {'not': 1, 'to': 2, 'or': 1, 'be': 2})

理想情况下,我可以用 C# 编写:

var frequencies = new DefaultDictionary<string,int>(() => 0);
foreach(string word in words)
{
    frequencies[word] += 1
}

【问题讨论】:

  • defaultdict 有什么作用?我对python不熟悉。编辑:它只是一个具有键默认值的字典。您可以继承 Dictionary 并实现该功能。
  • Jon Skeet 的回答可能会有所帮助:stackoverflow.com/a/2601501/1786606

标签: c# .net


【解决方案1】:

我认为没有等价物,但根据您的示例,您可以使用 LINQ 做到这一点:

var words = new List<string>{ "One", "Two", "Three", "One" };
var frequencies = words.GroupBy (w => w).ToDictionary (w => w.Key, w => w.Count());

【讨论】:

    【解决方案2】:

    让你开始的东西。我基本上只是更改了this 索引器。由于我不知道 python 的defaultdict 的完整功能,我无法进一步改进它。您给定的示例将起作用。

    public class DefaultDictionary<TKey, TValue> : IDictionary<TKey,TValue>
    {
        private readonly Func<TValue> _defaultSelector;
        private readonly Dictionary<TKey, TValue> _values = new Dictionary<TKey, TValue>();
    
        public DefaultDictionary(Func<TValue> defaultSelector)
        {
            _defaultSelector = defaultSelector;
        }
    
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _values.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            ((IDictionary<TKey,TValue>)_values).Add(item);
        }
    
        public void Clear()
        {
            _values.Clear();
        }
    
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return ((IDictionary<TKey,TValue>)_values).Contains(item);
        }
    
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            ((IDictionary<TKey, TValue>)_values).CopyTo(array, arrayIndex);
        }
    
        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return ((IDictionary<TKey, TValue>)_values).Remove(item);
        }
    
        public int Count { get { return _values.Count; } }
        public bool IsReadOnly { get { return ((IDictionary<TKey, TValue>) _values).IsReadOnly; } }
        public bool ContainsKey(TKey key)
        {
            return _values.ContainsKey(key);
        }
    
        public void Add(TKey key, TValue value)
        {
            _values.Add(key, value);
        }
    
        public bool Remove(TKey key)
        {
            return _values.Remove(key);
        }
    
        public bool TryGetValue(TKey key, out TValue value)
        {
            return _values.TryGetValue(key, out value);
        }
    
        public TValue this[TKey key]
        {
            get
            {
                if (!_values.ContainsKey(key))
                {
                    _values.Add(key, _defaultSelector());
                }
                return _values[key];
            }
            set
            {
                if(!_values.ContainsKey(key))
                {
                    _values.Add(key, _defaultSelector());
                }
                _values[key] = value;
            }
        }
    
        public ICollection<TKey> Keys { get { return _values.Keys; } }
        public ICollection<TValue> Values { get { return _values.Values; } }
    
        public Dictionary<TKey, TValue> ToDictionary()
        {
            return new Dictionary<TKey, TValue>(_values);
        }
    }
    

    【讨论】:

      【解决方案3】:

      这是一个简单的实现:

      public class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TValue : new()
      {
          public new TValue this[TKey key]
          {
              get
              {
                  TValue val;
                  if (!TryGetValue(key, out val))
                  {
                      val = new TValue();
                      Add(key, val);
                  }
                  return val;
              }
              set { base[key] = value; }
          }
      }
      

      以及你将如何使用它:

      var dict = new DefaultDictionary<string, int>();
      Debug.WriteLine(dict["foo"]);  // prints "0"
      dict["bar"] = 5;
      Debug.WriteLine(dict["bar"]);  // prints "5"
      

      或者像这样:

      var dict = new DefaultDictionary<string, List<int>>();
      dict["foo"].Add(1);
      dict["foo"].Add(2);
      dict["foo"].Add(3);
      

      【讨论】:

      • 你如何使这项工作多态。换句话说,我做IDictionary&lt;T,V&gt; mydict = new DefaultDict&lt;T,V&gt;()this[] 运算符不会被调用。
      【解决方案4】:

      ConcurrentDictionary(在 System.Collections.Generic 中)的行为非常相似(尽管是为并发使用而设计的)

      • 要检索值: GetOrAdd 方法返回一个键的值,如果它不存在,则使用值工厂创建一个。

      • 设置值: AddOrUpdate 方法更新一个值,如果它不存在则设置它

      优点:

      • 线程安全
      • 完全控制默认值和更新步骤

      缺点:

      • 稍微冗长的语法

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-09-26
        • 1970-01-01
        • 2011-08-15
        • 2011-04-30
        • 2018-05-22
        • 2012-01-15
        • 2011-07-06
        相关资源
        最近更新 更多