c++11的序列zip函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8511035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sequence-zip function for c++11?
提问by Hooked
With the new range-based for loop we can write code like
使用新的基于范围的 for 循环,我们可以编写如下代码
for(auto x: Y) {}
Which IMO is a hugeimprovement from (for ex.)
哪个 IMO 是(例如)的巨大改进
for(std::vector<int>::iterator x=Y.begin(); x!=Y.end(); ++x) {}
Can it be used to loop over two simultaneous loops, like Pythons zip
function? For those unfamiliar with Python, the code:
它可以用于循环两个同时进行的循环,比如 Pythonszip
函数吗?对于不熟悉 Python 的人,代码:
Y1 = [1,2,3]
Y2 = [4,5,6,7]
for x1,x2 in zip(Y1,Y2):
print x1,x2
Gives as output (1,4) (2,5) (3,6)
作为输出给出 (1,4) (2,5) (3,6)
采纳答案by kennytm
Warning:boost::zip_iterator
and boost::combine
as of Boost 1.63.0 (2016 Dec 26) will cause undefined behavior if the length of the input containers are not the same (it may crash or iterate beyond the end).
警告:boost::zip_iterator
和boost::combine
作为升压1.63.0(2016年12月26)将导致未定义的行为的,如果输入容器的长度是不相同的(它可能会崩溃或者iterate超过端)。
Starting from Boost 1.56.0 (2014 Aug 7) you could use boost::combine
(the function exists in earlier versions but undocumented):
从 Boost 1.56.0(2014 年 8 月 7 日)开始,您可以使用boost::combine
(该功能存在于早期版本中但未记录):
#include <boost/range/combine.hpp>
#include <vector>
#include <list>
#include <string>
int main() {
std::vector<int> a {4, 5, 6};
double b[] = {7, 8, 9};
std::list<std::string> c {"a", "b", "c"};
for (auto tup : boost::combine(a, b, c, a)) { // <---
int x, w;
double y;
std::string z;
boost::tie(x, y, z, w) = tup;
printf("%d %g %s %d\n", x, y, z.c_str(), w);
}
}
This would print
这将打印
4 7 a 4 5 8 b 5 6 9 c 6
In earlier versions, you could define a range yourself like this:
在早期版本中,您可以像这样自己定义一个范围:
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>
template <typename... T>
auto zip(T&&... containers) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(containers)...))>>
{
auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...));
auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...));
return boost::make_iterator_range(zip_begin, zip_end);
}
The usage is the same.
用法是一样的。
回答by aaronman
So I wrote this zip before when I was bored, I decided to post it because it's different than the others in that it doesn't use boost and looks more like the c++ stdlib.
所以我之前在无聊的时候写了这个zip,我决定发布它,因为它与其他的不同,它不使用boost,看起来更像c++ stdlib。
template <typename Iterator>
void advance_all (Iterator & iterator) {
++iterator;
}
template <typename Iterator, typename ... Iterators>
void advance_all (Iterator & iterator, Iterators& ... iterators) {
++iterator;
advance_all(iterators...);
}
template <typename Function, typename Iterator, typename ... Iterators>
Function zip (Function func, Iterator begin,
Iterator end,
Iterators ... iterators)
{
for(;begin != end; ++begin, advance_all(iterators...))
func(*begin, *(iterators)... );
//could also make this a tuple
return func;
}
Example use:
使用示例:
int main () {
std::vector<int> v1{1,2,3};
std::vector<int> v2{3,2,1};
std::vector<float> v3{1.2,2.4,9.0};
std::vector<float> v4{1.2,2.4,9.0};
zip (
[](int i,int j,float k,float l){
std::cout << i << " " << j << " " << k << " " << l << std::endl;
},
v1.begin(),v1.end(),v2.begin(),v3.begin(),v4.begin());
}
回答by Alexandre C.
You can use a solution based on boost::zip_iterator
. Make a phony container class maintaining references to your containers, and which return zip_iterator
from the begin
and end
member functions. Now you can write
您可以使用基于boost::zip_iterator
. 创建一个假容器类来维护对容器的引用,并zip_iterator
从begin
和end
成员函数返回。现在你可以写
for (auto p: zip(c1, c2)) { ... }
Example implementation (please test):
示例实现(请测试):
#include <iterator>
#include <boost/iterator/zip_iterator.hpp>
template <typename C1, typename C2>
class zip_container
{
C1* c1; C2* c2;
typedef boost::tuple<
decltype(std::begin(*c1)),
decltype(std::begin(*c2))
> tuple;
public:
zip_container(C1& c1, C2& c2) : c1(&c1), c2(&c2) {}
typedef boost::zip_iterator<tuple> iterator;
iterator begin() const
{
return iterator(std::begin(*c1), std::begin(*c2));
}
iterator end() const
{
return iterator(std::end(*c1), std::end(*c2));
}
};
template <typename C1, typename C2>
zip_container<C1, C2> zip(C1& c1, C2& c2)
{
return zip_container<C1, C2>(c1, c2);
}
I leave the variadic version as an excellent exercise to the reader.
我将可变参数版本作为一个很好的练习留给读者。
回答by Jonathan Wakely
See <redi/zip.h>
for a zip
function which works with range-base for
and accepts any number of ranges, which can be rvalues or lvalues and can be different lengths (iteration will stop at the end of the shortest range).
请参阅与 range-base 一起使用并接受任意数量范围<redi/zip.h>
的zip
函数,这些for
范围可以是右值或左值,并且可以是不同的长度(迭代将在最短范围的末尾停止)。
std::vector<int> vi{ 0, 2, 4 };
std::vector<std::string> vs{ "1", "3", "5", "7" };
for (auto i : redi::zip(vi, vs))
std::cout << i.get<0>() << ' ' << i.get<1>() << ' ';
Prints 0 1 2 3 4 5
印刷 0 1 2 3 4 5
回答by Venki
std::transformcan do this trivially:
std::transform可以简单地做到这一点:
std::vector<int> a = {1,2,3,4,5};
std::vector<int> b = {1,2,3,4,5};
std::vector<int>c;
std::transform(a.begin(),a.end(), b.begin(),
std::back_inserter(c),
[](const auto& aa, const auto& bb)
{
return aa*bb;
});
for(auto cc:c)
std::cout<<cc<<std::endl;
If the second sequence is shorter, my implementation seems to be giving default initialized values.
如果第二个序列更短,我的实现似乎给出了默认的初始化值。
回答by csguth
With range-v3:
使用range-v3:
#include <range/v3/all.hpp>
#include <vector>
#include <iostream>
namespace ranges {
template <class T, class U>
std::ostream& operator << (std::ostream& os, common_pair<T, U> const& p)
{
return os << '(' << p.first << ", " << p.second << ')';
}
}
using namespace ranges::v3;
int main()
{
std::vector<int> a {4, 5, 6};
double b[] = {7, 8, 9};
std::cout << view::zip(a, b) << std::endl;
}
The output:
输出:
[(4, 7),(5, 8),(6, 9)]
[(4, 7),(5, 8),(6, 9)]
回答by cshelton
I ran into this same question independently and didn't like the syntax of any of the above. So, I have a short header file that essentially does the same as the boost zip_iterator but has a few macros to make the syntax more palatable to me:
我独立地遇到了同样的问题,并且不喜欢上述任何一个的语法。所以,我有一个简短的头文件,它本质上与 boost zip_iterator 的作用相同,但有一些宏使语法更适合我:
https://github.com/cshelton/zipfor
https://github.com/cshelton/zipfor
For example you can do
例如你可以做
vector<int> a {1,2,3};
array<string,3> b {"hello","there","coders"};
zipfor(i,s eachin a,b)
cout << i << " => " << s << endl;
The main syntactic sugar is that I can name the elements from each container. I also include a "mapfor" that does the same, but for maps (to name the ".first" and ".second" of the element).
主要的语法糖是我可以命名每个容器中的元素。我还包括一个“mapfor”,它的作用相同,但用于地图(命名元素的“.first”和“.second”)。
回答by squid
// declare a, b
BOOST_FOREACH(boost::tie(a, b), boost::combine(list_of_a, list_of_b)){
// your code here.
}
回答by lorro
If you like operator overloading, here are three possibilities. The first two are using std::pair<>
and std::tuple<>
, respectively, as iterators; the third extends this to range-based for
. Note that not everyone will like these definitions of the operators, so it's best to keep them in a separate namespace and have a using namespace
in the functions (not files!) where you'd like to use these.
如果您喜欢运算符重载,这里有三种可能性。前两个分别使用std::pair<>
和std::tuple<>
作为迭代器;第三个将其扩展到基于范围的for
. 请注意,并非每个人都会喜欢这些运算符的定义,因此最好将它们保存在单独的命名空间中,并using namespace
在要使用它们的函数(而不是文件!)中放置一个。
#include <iostream>
#include <utility>
#include <vector>
#include <tuple>
// put these in namespaces so we don't pollute global
namespace pair_iterators
{
template<typename T1, typename T2>
std::pair<T1, T2> operator++(std::pair<T1, T2>& it)
{
++it.first;
++it.second;
return it;
}
}
namespace tuple_iterators
{
// you might want to make this generic (via param pack)
template<typename T1, typename T2, typename T3>
auto operator++(std::tuple<T1, T2, T3>& it)
{
++( std::get<0>( it ) );
++( std::get<1>( it ) );
++( std::get<2>( it ) );
return it;
}
template<typename T1, typename T2, typename T3>
auto operator*(const std::tuple<T1, T2, T3>& it)
{
return std::tie( *( std::get<0>( it ) ),
*( std::get<1>( it ) ),
*( std::get<2>( it ) ) );
}
// needed due to ADL-only lookup
template<typename... Args>
struct tuple_c
{
std::tuple<Args...> containers;
};
template<typename... Args>
auto tie_c( const Args&... args )
{
tuple_c<Args...> ret = { std::tie(args...) };
return ret;
}
template<typename T1, typename T2, typename T3>
auto begin( const tuple_c<T1, T2, T3>& c )
{
return std::make_tuple( std::get<0>( c.containers ).begin(),
std::get<1>( c.containers ).begin(),
std::get<2>( c.containers ).begin() );
}
template<typename T1, typename T2, typename T3>
auto end( const tuple_c<T1, T2, T3>& c )
{
return std::make_tuple( std::get<0>( c.containers ).end(),
std::get<1>( c.containers ).end(),
std::get<2>( c.containers ).end() );
}
// implement cbegin(), cend() as needed
}
int main()
{
using namespace pair_iterators;
using namespace tuple_iterators;
std::vector<double> ds = { 0.0, 0.1, 0.2 };
std::vector<int > is = { 1, 2, 3 };
std::vector<char > cs = { 'a', 'b', 'c' };
// classical, iterator-style using pairs
for( auto its = std::make_pair(ds.begin(), is.begin()),
end = std::make_pair(ds.end(), is.end() ); its != end; ++its )
{
std::cout << "1. " << *(its.first ) + *(its.second) << " " << std::endl;
}
// classical, iterator-style using tuples
for( auto its = std::make_tuple(ds.begin(), is.begin(), cs.begin()),
end = std::make_tuple(ds.end(), is.end(), cs.end() ); its != end; ++its )
{
std::cout << "2. " << *(std::get<0>(its)) + *(std::get<1>(its)) << " "
<< *(std::get<2>(its)) << " " << std::endl;
}
// range for using tuples
for( const auto& d_i_c : tie_c( ds, is, cs ) )
{
std::cout << "3. " << std::get<0>(d_i_c) + std::get<1>(d_i_c) << " "
<< std::get<2>(d_i_c) << " " << std::endl;
}
}
回答by knedlsepp
If you have a C++14 compliant compiler (e.g. gcc5) you can use zip
provided in the cppitertools
library by Ryan Haining, which looks really promising:
如果你有一个 C++14 兼容的编译器(例如 gcc5),你可以使用Ryan Hainingzip
在cppitertools
库中提供的,这看起来很有希望:
array<int,4> i{{1,2,3,4}};
vector<float> f{1.2,1.4,12.3,4.5,9.9};
vector<string> s{"i","like","apples","alot","dude"};
array<double,5> d{{1.2,1.2,1.2,1.2,1.2}};
for (auto&& e : zip(i,f,s,d)) {
cout << std::get<0>(e) << ' '
<< std::get<1>(e) << ' '
<< std::get<2>(e) << ' '
<< std::get<3>(e) << '\n';
std::get<1>(e)=2.2f; // modifies the underlying 'f' array
}