为什么范围不能用于管道库功能?
Why can ranges not be used for the pipes library functionality?
Jonathan Boccara(Fluent C++) wrote a library called pipes.
的作者
这个 "piping",存储库的主页说,它不像范围的使用,即使它看起来是一样的:它不是基于懒惰的拉动,而是急切的推动。但是 stated 不能使用范围库执行各种 'pipe' 操作。例如:
- unzip - 采用压缩输入 - 本质上是一系列 k 元组 - 并产生 k 个独立的输出。
- fork - 生成 container/range.
的多个(独立)副本
不太明白为什么原则上是这样。 (当然,你无法到达终点的范围除外iterator/sentinel。)
讨论的本质上是基于推的处理方法和基于拉的处理方法之间的区别。在像这个管道库这样的推送系统中,您建立了一个处理链,每个处理步骤将其数据直接推送到下一个步骤。在像范围这样的拉式系统中,您建立了数据表示,您可以根据需要访问和修改它。处理不会自行发生;只有当有人试图消耗范围时才会发生。
unzip
和 fork
操作都是一对多操作:它们采用单个输入并将其映射到多个处理操作。
作为推送系统,管道库可以处理一对多操作,因为它的结构 API。一个操作由一个函数调用表示;输入由使用点暗示(使用 >>=
或将其传递给处理器)。函数的参数定义了它的输出(忽略处理器本身的参数)。由于 C++ 函数可以有任意数量的参数,一对多的映射操作自然会失效。您只需为各种输出提供适当的处理器。
作为拉式系统,范围基于 return 值。 C++ 没有 returning 多个值的语言机制,所以我们能做的最好的是 return 一个 "value" 表示多个值。
但是,范围适配器链接最终基于输入范围。而“代表多个值的'value'”是本身而不是一个范围。它可能包含范围,但这并不能使它成为一个范围。
所以现在您必须非常明确地采用这种 "not a range" 类型,并使您所有的范围适配器都可以使用它。应用范围适配器必须跨类型广播该操作,从而创建多对多操作。做到这一点并不容易。
但更重要的是...这可能不是您想要的。如果你 fork
一个范围,那么你几乎肯定想对复制的范围进行不同的处理。这完全关闭了使用 |
操作执行此操作的任何机会。您必须构建将适配器应用于这些范围元组的特定部分的方法。这些方式越来越像基于推送的处理器。
归根结底,拉式系统在每一层只有一个输出。这只是这种 API 的核心概念的一部分:每个处理步骤都会生成 a 范围。这有它的优点(惰性处理),但表示一对多操作是它的弱点之一。
范围当然可以有一个 unzip
函数(fork
实际上只是复制范围)。但它不会是 |
风格的适配器;它将是一个函数,它接受一些可分解类型的范围,并且 returns 是一个范围元组。如果您想对它们进行更多处理,则需要将元组存储在一个值中,访问各个元素,并根据需要使用它们。
Jonathan Boccara(Fluent C++) wrote a library called pipes.
的作者这个 "piping",存储库的主页说,它不像范围的使用,即使它看起来是一样的:它不是基于懒惰的拉动,而是急切的推动。但是 stated 不能使用范围库执行各种 'pipe' 操作。例如:
- unzip - 采用压缩输入 - 本质上是一系列 k 元组 - 并产生 k 个独立的输出。
- fork - 生成 container/range. 的多个(独立)副本
不太明白为什么原则上是这样。 (当然,你无法到达终点的范围除外iterator/sentinel。)
讨论的本质上是基于推的处理方法和基于拉的处理方法之间的区别。在像这个管道库这样的推送系统中,您建立了一个处理链,每个处理步骤将其数据直接推送到下一个步骤。在像范围这样的拉式系统中,您建立了数据表示,您可以根据需要访问和修改它。处理不会自行发生;只有当有人试图消耗范围时才会发生。
unzip
和 fork
操作都是一对多操作:它们采用单个输入并将其映射到多个处理操作。
作为推送系统,管道库可以处理一对多操作,因为它的结构 API。一个操作由一个函数调用表示;输入由使用点暗示(使用 >>=
或将其传递给处理器)。函数的参数定义了它的输出(忽略处理器本身的参数)。由于 C++ 函数可以有任意数量的参数,一对多的映射操作自然会失效。您只需为各种输出提供适当的处理器。
作为拉式系统,范围基于 return 值。 C++ 没有 returning 多个值的语言机制,所以我们能做的最好的是 return 一个 "value" 表示多个值。
但是,范围适配器链接最终基于输入范围。而“代表多个值的'value'”是本身而不是一个范围。它可能包含范围,但这并不能使它成为一个范围。
所以现在您必须非常明确地采用这种 "not a range" 类型,并使您所有的范围适配器都可以使用它。应用范围适配器必须跨类型广播该操作,从而创建多对多操作。做到这一点并不容易。
但更重要的是...这可能不是您想要的。如果你 fork
一个范围,那么你几乎肯定想对复制的范围进行不同的处理。这完全关闭了使用 |
操作执行此操作的任何机会。您必须构建将适配器应用于这些范围元组的特定部分的方法。这些方式越来越像基于推送的处理器。
归根结底,拉式系统在每一层只有一个输出。这只是这种 API 的核心概念的一部分:每个处理步骤都会生成 a 范围。这有它的优点(惰性处理),但表示一对多操作是它的弱点之一。
范围当然可以有一个 unzip
函数(fork
实际上只是复制范围)。但它不会是 |
风格的适配器;它将是一个函数,它接受一些可分解类型的范围,并且 returns 是一个范围元组。如果您想对它们进行更多处理,则需要将元组存储在一个值中,访问各个元素,并根据需要使用它们。