【问题标题】:Jagged dictionary of dynamic depth?动态深度的锯齿状字典?
【发布时间】:2013-04-02 12:00:13
【问题描述】:

我正在尝试从一系列代表深度的字符串中提取:

'foo/bar/x'
'foo/bar/baz/x'
'foo/bar/baz/x'
'foo/bar/lol/x'

x 是一个我不关心的数字。我已经在/ 上拆分并循环遍历,此时在 PHP 中我会做一些事情,比如检查我在循环中的位置(使用for (i=0; etc)),然后用它来确定我的深度构建一个输出数组,如:

output['foo']['bar'] = 1
output['foo']['bar']['baz'] = 2
output['foo']['bar']['lol'] = 1

问题在于“深度”是动态的,可能只有 3/4 深(我可以通过对 i 的值进行大量检查并单独处理它们来解释)或者说 10 或更深在这种情况下,某种递归函数可能是最好的。

我遇到了一个问题,将字符串作为数组索引我需要使用字典,但是您必须指定字典中的类型,这意味着您需要了解高级深度(如果我错了)在实例化字典对象时。

我猜一个攻击可能类似于调用一个递归函数,这样每次调用它时你传递i 来指示深度,然后函数调用自己每次递减i,直到它构建了来自该输入字符串的树,但我不确定它是我在 C# 中使用的存储结构。

最终输出将是一个 CSV,我可以将其作为电子表格打开,如下所示:

Foo        0
|__Bar     1
   |__Baz  2
   |__Lol  1

也许解决方案的一个方向是使用纯 C# 数组并简单地将标题(例如 foo)存储在其中,将信息保留在数组索引之外,这可能是最好的做法。谢谢。

【问题讨论】:

  • 即使在PHP中,如果output['foo']['bar'] = 1output['foo']['bar']['baz']怎么可能存在? output['foo']['bar'] 要么是字典,要么是值。我并不是说这是不可能的,但这不是一个好主意......也考虑一下:var whatAmI = output['foo']['bar'];whatAmI == 1whatAmI is Dictionary?

标签: c# recursion jagged-arrays recursive-datastructures


【解决方案1】:

您可以使用以下成员创建自己的类:

class Directory
{
    public int Value { get; set; }
    public Dictionary<string, Directory> SubDirectories { get; set; }
}

使用它存储您的数据,然后以递归方式将其导出为 CSV。

要获得output["foo"]["bar"] 语法可能在你的类中实现索引器:

public Directory this[string name]
{
    get { return SubDirectories.ContainsKey("name") ? SubDirectories[key] : null; }
    set { SubDirectories.Add(name, value); }
}

【讨论】:

  • 实现某种 Tree 数据结构可能会更简单。
  • 它是某种树,SubDirectories 属性持有叶子,不是吗?
  • 子目录没有引用它的父目录。
  • 这种情况下我觉得没必要,但是很容易解决。
  • @Romoku - 在树中,你不必有一个指向你父母的指针。例如:en.wikipedia.org/wiki/Binary_search_tree
【解决方案2】:

虽然 Marcin Juraszek 的解决方案很棒,但我只想用 dynamic 糖稍微扩展他的答案。事实并非如此,该解决方案将满足您的需求,而只是将其视为示例。我将使Directory&lt;T&gt; 通用,因此您可以为value 使用任何类型(请注意,由于动态性质,我在实现(T)value 中有一个演员)

class Directory<T> : DynamicObject
{
    private T Value;
    private Dictionary<string, Directory<T>> SubDirectories;

    public Directory()
    {
        SubDirectories = new Dictionary<string, Directory<T>>();
    }

    public override bool TryGetMember(GetMemberBinder binder, out Object result)
    {
        if (!SubDirectories.ContainsKey(binder.Name))
            SubDirectories[binder.Name] = new Directory<T>();

        result = SubDirectories[binder.Name];   
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, Object value)
    {
        if (!SubDirectories.ContainsKey(binder.Name))
            SubDirectories[binder.Name] = new Directory<T>();

        SubDirectories[binder.Name].Value = (T)value;
        return true;
    }

    public override string ToString()
    {
        return Value.ToString();
    }
} 

现在您可以使用 C# 4.0

提供的dynamic 功能
dynamic dir = new Directory<string>();

dir.foo = "Foo Value";
dir.foo.bar = "Bar Value";
dir.foo.bar.baz = "baz value";
dir.foo.bar.Lol = "Lol value";

Console.WriteLine(dir.foo.bar.Lol); //will print Lol value
Console.WriteLine(dir.foo.bar.baz); //will print baz value

这是:

Foo        Foo Value
|__Bar     Bar Value
   |__Baz  baz value
   |__Lol  Lol value

你也可以重写TryGetIndexTrySetIndex,这样你就可以传递复杂的字符串,在C#中不能作为属性使用

【讨论】:

  • 使用ToString 有点作弊,不是吗?此外,您不能拥有名为“Value”或“SubDirectories”的目录。接下来,我们甚至可以更丑:public static implicit operator T(Directory&lt;T&gt; m){return m.Value;} 将启用string s = dir.foo.bar;。正如我在评论中所说 - 这不是一个好主意......
  • @Kobi 好注意,你绝对应该将内部结构设为私有,然后你可以dir.SubDirectories = "Foo Value";。此外,隐式运算符 T 不是最好的主意,但在与静态结构集成时,动态也是很棘手的。我使用Value.ToString(),所以内部Value 可以在ToString 上有复杂的逻辑。无论如何,我已经注意到这只是一个演示或示例,而不是生产代码。
猜你喜欢
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 1970-01-01
  • 2017-03-21
相关资源
最近更新 更多