【问题标题】:Returning a string from an IEnumerable<T>function从 IEnumerable<T> 函数返回字符串
【发布时间】:2021-12-30 09:27:23
【问题描述】:

我可能做错了什么,但我正在尝试做this Kata on Codewars

下面是我当前的代码。

public static class Kata
{
 public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> arr) 
 {
   Type t = typeof(T);
   if (t == typeof(string))
     return (IEnumerable<T>)String.Join("",arr.Distinct()).AsEnumerable();
   
   return arr.Distinct().ToArray();
 }
}

此 kata 的单元测试期望输入“AAAABBBCCDAABBB”返回为“ABCDAB”。

我上面的代码由于这个错误而失败 Expected is &lt;System.String&gt;, actual is &lt;System.Char[6]&gt;

如果我尝试返回一个字符串,我会收到以下错误:error CS0029: Cannot implicitly convert type 'string' to 'System.Collections.Generic.IEnumerable&lt;T&gt;'

如果我无法返回字符串(并且 char 数组失败),我如何返回预期的字符串?

谢谢

【问题讨论】:

  • 你传递了一个string,这是一个IEnumerable&lt;char&gt;,而不是IEnumerable&lt;string&gt;,这意味着Tchar而不是string,所以你的if (t == typeof(string))返回false
  • 请注意,您不能简单地通过调用.Distinct() 来解决此问题;如果是这样,就没有挑战了。
  • 你的返回类型是IEnumerable&lt;T&gt;,但你断言它是string
  • 另外......没有理由打电话给ToArray()。它只是浪费内存和cpu。 Distinct() 方法本身已经完成了您的 IEnumerable 合同。如果调用者实际上想要一个数组(或列表,或其他),让他们做出选择。
  • 用老式的方法来做。循环遍历 IEnumerable,如果元素 i 与元素 i-1 相同,则将其丢弃。如果没有,yield return

标签: c# algorithm ienumerable


【解决方案1】:

应该这样做:

public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) 
{
    using var e = iterable.GetEnumerator();
    bool EnumeratorActive = e.MoveNext(); //start iterating
    while (EnumeratorActive)
    {
        // set and yield the current value
        T cur = e.Current; 
        yield return cur;

        // keep advancing while the iterator is active and additional values match the current val
        while(cur.Equals(e.Current) && (EnumeratorActive = e.MoveNext()))
        {} //empty body intentional
    }    
}

它使用称为 Control/Break 的嵌套 while 循环模式,该模式仍以线性时间运行(因为只有内部循环在前进)。

这是一个没有额外解释的简化版本:

public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) 
{
    using var e = iterable.GetEnumerator();
    bool EnumeratorActive = e.MoveNext();
    while (EnumeratorActive)
    {
        T cur = e.Current; 
        yield return cur;
        while(cur.Equals(e.Current) && (EnumeratorActive = e.MoveNext()));
    }    
}

【讨论】:

  • 如果序列包含 null 的元素(这可能是允许的;至少没有理由假设它不是),则此操作失败。如果你不打算使用EqualityComparer,你也可以使用静态Object.Equals
【解决方案2】:

你可以这样实现它(因为你已经使用了&lt;T&gt;,让我们用自定义comparer实现一般情况解决方案):

public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> source, 
                                              IEqualityComparer<T> comparer = null) {
  if (null == source)
    throw new ArgumentNullException(nameof(source));

  comparer ??= EqualityComparer<T>.Default;

  bool first = true;
  T prior = default; // default: let compiler be happy

  foreach (T item in source)
    if (first || !comparer.Equals(item, prior)) {
      prior = item;
      first = false;

      yield return item;
    }
}

这里我们只检查当前item是否等于prior

演示:

string source = "AAAABBBCCDAABBB";

string result = string.Concat(UniqueInOrder(source)); 

Console.Write(result);

结果:

ABCDAB

编辑:请注意

中的Distinct()
arr.Distinct()

删除整个 arr 中的重复项,这就是为什么你只会得到 4 个不同的字符:

AAAABBBCCDAABBB  ->  ABCD
             Distinct()

在给定的问题中,我们有一个较弱条件:当前项不能等于先前项。

【讨论】:

  • 不错!我只是在测试他们的测试:Expected is &lt;System.String&gt;, actual is &lt;System.Linq.Enumerable+DistinctIterator1[System.Char]>` 所以让我们教初学者Distinct 的奇妙用法,然后跳出惊人的循环来获得满足测试的正确返回类型。他们为什么要将字符串交给这样的方法。这在现实世界中不会发生……除非您使用的是 Java。 (我使用 java,没有深入了解火焰战争。:P)
猜你喜欢
  • 2022-12-03
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 2010-12-14
  • 2014-12-07
  • 2014-11-06
相关资源
最近更新 更多