如何设计一个(理论上)返回无限数量项的枚举器

本文关键字:无限 返回 枚举 一个 理论上 | 更新日期: 2023-09-27 17:48:54

我正在编写类似于以下内容的代码:

public IEnumerable<T> Unfold<T>(this T seed)
{
    while (true)
    {
        yield return [next (T)object in custom sequence];
    }
}

显然,这种方法永远不会返回。(C#编译器默默地允许这样做,而R#则向我发出警告"函数永远不会返回"。)

一般来说,提供一个返回无限多个项的枚举器,而不提供停止枚举的方法,这是不是糟糕的设计?

这种情况有什么特别的考虑吗?Mem?Perf?其他问题?

如果我们总是提供一个退出条件,有哪些选项?例如:

  • T类型的对象,表示包含或排除边界
  • Predicate<T> continue(与TakeWhile一样)
  • 计数(与Take一样)

我们是否应该依赖用户在Unfold(...)之后调用Take(...)/TakeWhile(...)?(也许是首选,因为它利用了现有的Linq知识。)

如果代码将在公共API中发布,无论是作为i(通用)还是作为该模式的特定实现,您会以不同的方式回答这个问题吗?

如何设计一个(理论上)返回无限数量项的枚举器

只要你非常清楚地记录该方法永远不会完成迭代(当然,方法本身返回得很快),那么我认为这很好。事实上,它可以使一些算法更加整洁。我不认为有任何显著的内存/perf含义——尽管如果你在迭代器中引用一个"昂贵"的对象,那么这个引用就会被捕获。

滥用API总是有方法的:只要你的文档是清楚的,我认为这是好的。

"一般来说,设计不好吗提供返回的枚举器无限数量的项目,没有提供一种停止枚举的方法?"

代码的使用者可以始终停止枚举(例如使用break或其他方式)。如果您的枚举器返回一个无限序列,这并不意味着枚举器的客户端在某种程度上被迫永远不会破坏枚举,实际上您无法制作一个保证由客户端完全枚举的枚举器。

我们应该依靠用户呼叫吗Take(…)/TakeWhile(…)之后展开(…)?(也许是首选选项,因为它利用了现有林克知识。)

是的,只要您在文档中明确指定枚举器返回,并且枚举的无限序列和中断是调用方的责任,一切都应该很好。

返回无限序列并不是一个坏主意,函数式编程语言已经做了很长时间了。

我同意Jon的观点。编译器将您的方法转换为实现简单状态机的类,该类保持对当前值(即将通过current属性返回的值)的引用。我多次使用这种方法来简化代码。若您清楚地记录方法的行为,它应该可以正常工作。

我不会在公共API中使用无限枚举器。C#程序员,包括我自己,都太习惯foreach循环了。这也将与.NET框架保持一致;请注意Enumerable.Range和Enumerable.Erepeat方法如何使用参数来限制Enumerable中的项数。微软选择使用Enumerable.Repeat(",10)而不是Enumerable.Depeat"(").Take(10)来避免无限枚举,我会坚持他们的设计选择。