6

我正在编写类似于此的代码:

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

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

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

这种情况有什么特殊的考虑吗?内存?性能?其他陷阱?

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

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

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

如果代码将按原样(通用)或作为此模式的特定实现在公共 API 中发布,您会以不同的方式回答这个问题吗?

4

4 回答 4

7

只要您非常清楚地记录该方法永远不会完成迭代(当然,该方法本身返回得非常快),那么我认为这很好。事实上,它可以使一些算法更加整洁。我不相信有任何显着的内存/性能影响 - 尽管如果您在迭代器中引用“昂贵”对象,该引用将被捕获。

总是有滥用 API 的方法:只要您的文档清晰,我认为没问题。

于 2008-10-13T11:33:20.017 回答
6

“一般来说,提供一个返回无限数量项目的枚举器,而不提供停止枚举的方法是不是很糟糕?”

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

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

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

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

于 2008-10-13T11:47:18.337 回答
2

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

于 2008-10-13T12:03:17.273 回答
0

I would not use an infinite enumerator in a public API. C# programmers, myself included, are too used to the foreach loop. This would also be consistent with the .NET Framework; notice how the Enumerable.Range and Enumerable.Repeat methods take an argument to limit the number of items in the Enumerable. Microsoft chose to use Enumerable.Repeat(" ", 10) instead of Enumerable.Repeat(" ").Take(10) to avoid the infinite enumeration and I would adhere to their design choices.

于 2008-10-13T12:15:20.713 回答