【问题标题】:Traverse and Print Complex List遍历并打印复杂列表
【发布时间】:2018-05-12 05:46:58
【问题描述】:

方法签名:

public static string TraverseList(string prefix, object list)

输入:

prefix: "Boo"
list: new object[] { "a string", new[] { "a", "b", "c" }, "spam", new[] { "eggs" }, new[] { new[] { "one", "two" }, new[] { "three", "four" } } }

预期输出:

Boo.0: a string
Boo.1.0: a
Boo.1.1: b
Boo.1.2: c
Boo.2: spam
Boo.3.0: eggs
Boo.4.0.0: one
Boo.4.0.1: two
Boo.4.1.0: three
Boo.4.1.1: four

我尝试实现这一点:

public static string TraverseList(string prefix, object list)
{
    int index = 0;
    int index1 = 0;
    int index2 = 0;

    StringBuilder dumpListBuilder = new StringBuilder();

    if (list is Array)
    {
        foreach (var obj in (Array)list)
        {
            if (obj is Array)
            {
                foreach (var obj1 in (Array)obj)
                {
                    if (obj1 is Array)
                    {
                        foreach (var obj2 in (Array)obj1)
                        {
                            dumpListBuilder.AppendFormat("{0}.{1}.{2}.{3}: {4}{5}", prefix, index, index1, index2, obj2, Environment.NewLine);
                            index2++;
                        }
                    }
                    else
                    {
                        dumpListBuilder.AppendFormat("{0}.{1}.{2}: {3}{4}", prefix, index, index1, obj1, Environment.NewLine);
                    }
                    index1++;
                    index2 = 0;
                }
            }
            else
            {
                dumpListBuilder.AppendFormat("{0}.{1}: {2}{3}", prefix, index, obj, Environment.NewLine);
            }
            index++;
            index1 = 0;
        }
    }

    return dumpListBuilder.ToString();
}

它有效,但效率不高,并且仅限于 3 个嵌套列表的深度。任何人都可以提出无限深度的最佳解决方案吗?

我知道我应该使用递归,但是如何在不更改方法签名的情况下做到这一点?

【问题讨论】:

  • 提示:递归
  • 您无法更改原始转储列表签名,但您可以创建一个可以从 DumpList 调用并从其自身递归调用的进一步方法,不是吗?
  • @Gian Paolo 不需要任何进一步的方法
  • 对@SirRufo,我没有注意到您可以处理在每个递归级别更改它的前缀

标签: c# .net


【解决方案1】:

递归是这里的关键

static void Main( string[] args )
{
    var prefix = "Foo";
    var input = new object[] { "a string", new[] { "a", "b", "c" }, "spam", new[] { "eggs" }, new[] { new[] { "one", "two" }, new[] { "three", "four" } } };
    var output = DumpList( prefix, input );
    Console.WriteLine( output );
}

static string DumpList( string prefix, object list )
{
    var collection = !( list is string )
        ? list as IEnumerable
        : null;
    return collection != null
        ? string.Join(
            separator: Environment.NewLine,
            values: collection.Cast<object>().Select( ( o, i ) => DumpList( $"{prefix}.{i}", o ) ) )
        : $"{prefix}: {list}";
}

.Net Fiddle

【讨论】:

  • 您不需要使用否定条件,因为您可以处理这两种情况。否定是如此难以阅读brrr... ;)
【解决方案2】:

您可以使用recursion 使其更具可读性和逻辑性

我的

public static string DumpList(string prefix, object list)
{
    if (list is object[] array)
        return String.Join("\r\n", array.Select((value, i) => DumpList($"{prefix}.{i}", value)));
    return $"{prefix}: {list}";
}

我的原创

public static void DumpList2(string prefix, object input, List<string> result)
{
   if (input is object[] ary)
      for (var i = 0; i < ary.Length; i++)
         if (ary[i] is Array)
            DumpList2($"{prefix}.{i}", ary[i], result);
         else
            result.Add($"{prefix}.{i}.{ary[i]}");
   else
      result.Add($"{prefix}.0.{input}");
}

基准测试

Mode            : Release
Test Framework  : .NET Framework 4.7.1
Benchmarks runs : 100 times (averaged)

Scale : 100
Name         |     Time |    Range | StdDev |    Cycles | Pass
-----------------------------------------------------------------
MineOriginal | 0.550 ms | 0.311 ms |   0.15 | 1,853,187 | Yes
Original     | 0.612 ms | 0.361 ms |   0.13 | 2,059,844 | Base
Mine         | 0.697 ms | 0.012 ms |   0.08 | 2,368,041 | Yes
SirRufo      | 0.776 ms | 0.022 ms |   0.15 | 2,635,368 | Yes
Jimi         | 0.821 ms | 0.012 ms |   0.21 | 2,779,449 | Yes
JimiNoFormat | 0.873 ms | 0.007 ms |   0.23 | 2,950,638 | Yes


Scale : 1,000
Name         |     Time |    Range | StdDev |     Cycles | Pass
------------------------------------------------------------------
MineOriginal | 4.461 ms | 0.541 ms |   0.21 | 15,185,195 | Yes
Original     | 5.914 ms | 0.346 ms |   1.15 | 20,015,882 | Base
Mine         | 6.725 ms | 0.153 ms |   0.31 | 22,890,735 | Yes
JimiNoFormat | 6.818 ms | 0.335 ms |   0.23 | 23,193,642 | Yes
Jimi         | 6.873 ms | 0.633 ms |   0.37 | 23,382,543 | Yes
SirRufo      | 7.198 ms | 1.004 ms |   0.70 | 24,512,923 | Yes


Scale : 10,000
Name         |      Time |    Range | StdDev |      Cycles | Pass
--------------------------------------------------------------------
Original     | 57.283 ms | 3.049 ms |   5.70 | 194,331,610 | Base
MineOriginal | 67.833 ms | 5.618 ms |   2.66 | 230,951,862 | Yes
Mine         | 72.695 ms | 2.624 ms |   3.39 | 246,760,230 | Yes
Jimi         | 73.571 ms | 4.007 ms |   2.00 | 249,736,001 | Yes
JimiNoFormat | 73.659 ms | 2.204 ms |   2.92 | 249,493,594 | Yes
SirRufo      | 74.141 ms | 2.395 ms |   2.64 | 251,712,472 | Yes

备注

JimiNoFormat 只是一个使用他的代码和字符串插值的版本

Scale 基本上是原始数组加入并合并了很多次,以制作一个超级大小的数组进行测试

MineOriginal 基本上只是使用一个列表,并在最后将其转换为字符串,这比在整个过程中使用字符串更有效,但是它不能很好地扩展,最终会在非常大的数据集上丢失

总结

递归广告的开销更大,但还不错

【讨论】:

  • @GiladReich 哈哈,我在考虑它
  • 哈哈...我也是来做的:P
  • 你能对我刚刚发布的内容进行基准测试吗?它在 100 轮中给了我 25 毫秒,但当然它可能取决于硬件。
  • @Jimi 当然可以!
  • 嗯,这很有趣。空值检查几乎没有开销,for/foreach 循环仍然比简单的、非缓存的 Linq Select 更快。我来看看IL。也许这可以使用StringBuilder 和缓存的 Linq 扩展来完成。所以,你是官方的基准测试人员。也很高兴知道:)
【解决方案3】:

[For benchmarking purposes]
@Sir Rufo 编码的程序的详细说明,没有空检查:
(.NET Framework 4.7.1 - C# 7.3)

var prefix = "Foo";
var input = new object[] { "a string", new[] { "a", "b", "c" }, "spam", new[] { "eggs" }, new[] { new[] { "one", "two" }, new[] { "three", "four" } } };

string output = DumpList(prefix, input);
Console.WriteLine(output);


static string DumpList(string prefix, object list) => (list is string)
                       ? $"{prefix}: {list}"
                       : string.Join(Environment.NewLine,
                                   ((IEnumerable)list).Cast<object>().Select((obj, idx) =>
                                     DumpList($"{prefix}.{idx}", obj)));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-26
    • 2022-01-04
    • 2016-10-10
    • 2015-10-27
    • 2021-07-10
    • 2020-10-28
    • 2019-01-04
    • 1970-01-01
    相关资源
    最近更新 更多