作者
JonathanBoccara
译者
苏本如责编
胡巍巍
作者注:今天我们收到了一个来自亚历克斯·阿斯塔辛(AlexAstashyn)的客座帖子。Alex是美国国家生物技术信息中心的RefSeq(ReferenceSequence–标准序列)资源的技术负责人。
注:本文所表达的观点是该帖子作者的观点。而且,我自己也不能算是个“range专家”,所以有些与range有关的信息实际上可能是不正确的(如果你发现有任何严重错误,请在下面留下你的评论)。
在本文中,我将讨论我在C++range上遇到的问题和局限性。
我还将介绍我自己开发的库rangeless,它将所有我希望由range实现的功能提炼在一起,使得我能够处理更大范围的有趣的实际应用用例。
序言
和所有爱好面向函数的声明式无状态编程的开发者一样,我原以为ranges非常有前途。然而,在实践中使用它们的尝试,被证明是一个非常令人沮丧的经历。
我一直在尝试用range写出那些对我来说似乎很合理的代码,然而,编译器却不断地打印出一页页让我无法理解的错误消息。最后我意识到了我自己的错误。我原以为range和这样的UNIX管道一样:catfile
grep...
sed...
sort
uniq-c
sort-nr
head-n10,但事实并非如此……
示例:
示例1:Intersperse(插入分隔符)
让我们尝试编写一个view,在输入元素之间插入一个分隔符。
(此功能由range-v3提供,因此我们可以比较一下这些方法的不同)
//inputs:[x1,x2,...xn]//transform:[[x1,d],[x2,d],...[xn,d]]//flatten:[x1,d,x2,d,...xn,d]//droplast:[x1,d,x2,d,...xn]autointersperse_view=view::transform([delim](autoinp){returnstd::arraydecltype(inp),2{{std::move(inp),delim}};})
view::join//alsocalledconcatorflatteninfunctionallanguages
view:rop_last(1);//droptrailingdelim
transform
join上面的合成是对流的一种常见操作,该操作将每个输入转换为一个输出序列,并对结果序列进行展平。
[x]-(x-[y])-[y]
有些语言对此有单独的抽象,例如Elixir语言中的flat_map或LINQ中的SelectMany。
基于最小惊呀的原则,看来上述办法应该奏效。(如果你还没看过这篇演讲,那说明我推荐的力度还不够)。
但是,上面的代码与range-v3一起无法编译通过。出什么事了?结果发现问题在于view::join不喜欢subrange(子范围-返回的集合)是一个作为右值(rvalue)返回的容器这一事实。我想出了以下的技巧:(有时)用这些视图(view)的右值组成视图,所以让我们将容器返回值包装为一个视图!
view::transform([delim](autoinp){returnview:enerate_n([delim,inp,i=0]()mutable{return(i++==0)?inpelim;},2);})
或者,概括地说,我们可以返回一个容器,例如向量,作为其他用例的视图:
view::transform([](intx){autovec=...;returnview:enerate_n([i=0,vec=std::move(vec)]()mutable{returnstd::move(vec[i++]);},vec.size());})
view::join//nowjoin