本节例子选自: https://gist.github.com/JeffPaine/6213790
对 python 这样的动态语言最直观的感受就是 list/map 两种数据结构打天下。 php 和 lua 甚至把这两个都合并成一种数据结构了。 毋庸置疑,学会如何使用 list 和 map 是基础中的基础。
for 循环
Python 版本
1
2
3
4
5
6
| import unittest
class Test(unittest.TestCase):
def test_foreach_on_lazy_range(self):
for i in xrange(6):
print i ** 2
|
C++ 版本
1
2
3
4
5
6
7
8
9
10
| #include <catch_with_main.hpp>
#include <range/v3/all.hpp>
using namespace ranges;
TEST_CASE("foreach on lazy range") {
for(const auto& x : view::ints(0, 6)) {
std::cout << x * x << std::endl;
}
}
|
注意到 const auto& 的写法,这个表示我对这个变量进行只读的使用。只要是能用 const 的地方就用 const ( http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#a-namerconst-immutableacon1-by-default-make-objects-immutable)。 为什么还需要加上 reference ?因为非 reference 的版本默认的语义我要拥有这个变量( make a copy )。而在 for 循环下我们显然是只打算使用这个变量, 而不是去拥有一份。为什么不用指针而用引用?因为指针可空, reference 不可空。
view::ints 是 range-v3 这个库提供的,作用等同于 xrange 。将来 range-v3 会成为标准库的一部分。
foreach
Python 版本
1
2
3
4
5
6
7
| import unittest
class Test(unittest.TestCase):
def test_foreach_on_list(self):
colors = ['red', 'green', 'blue', 'yellow']
for color in colors:
print color
|
C++ 版本
1
2
3
4
5
6
7
8
9
10
| #include <catch_with_main.hpp>
using namespace ranges;
TEST_CASE("foreach on list") {
auto colors = {"red", "green", "blue", "yellow"};
for(const auto& color : colors) {
std::cout << color << std::endl;
}
}
|
与 python 不同, c++没有所谓的默认的 list 类型。上面的写法是最简洁的写法。 colors 变量的实际类型 根据 GDB 是 std::initializer_list<const char*>。只有 begin , end , size 几个函数。实际上类似于 python 的 tuple 。 考虑到 python 的 list 类型是 mutable 的,所以更合适的实现是 std::vector 。
1
2
3
4
5
6
7
8
9
10
| #include <catch_with_main.hpp>
using namespace ranges;
TEST_CASE("foreach on vector") {
auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
for(const auto& color : colors) {
std::cout << color << std::endl;
}
}
|
foreach 倒序
Python 版本
1
2
3
4
5
6
7
8
| import unittest
class Test(unittest.TestCase):
def test_foreach_reversed(self):
colors = ['red', 'green', 'blue', 'yellow']
for color in reversed(colors):
print(color)
|
C++ 版本
1
2
3
4
5
6
7
8
9
10
11
| #include <catch_with_main.hpp>
#include <range/v3/all.hpp>
using namespace ranges;
TEST_CASE("foreach reversed") {
auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
for(const auto& color : colors | view::reverse) {
std::cout << color << std::endl;
}
}
|
这里使用了 range-v3 的 view 组合,类似 unix pipe 的语法。
foreach 带下标
Python 版本
1
2
3
4
5
6
7
| import unittest
class Test(unittest.TestCase):
def test_foreach_with_index(self):
colors = ['red', 'green', 'blue', 'yellow']
for i, color in enumerate(colors):
print(i, color)
|
C++ 版本
1
2
3
4
5
6
7
8
9
10
11
| #include <catch_with_main.hpp>
#include <range/v3/all.hpp>
using namespace ranges;
TEST_CASE("foreach with index") {
auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
for(const auto& [i, color] : view::zip(view::iota(0), colors)) {
std::cout << i << " " << color << std::endl;
}
}
|
view::iota这个的意思是产生一个从 n 开始的逐个加一的 view ,类似 python 里的 generator 。然后 zip 是把两个 view 逐个对应起来合并成一个 pair 的 view 。 然后const auto& [i, color]是 c++ 17 的 structured bindings 的写法,和 python 解开 tuple 里的元素的做法是如出一辙的。
zip
下面这个例子可以看得更清楚。
Python 版本
1
2
3
4
5
6
7
8
9
| import unittest
import itertools
class Test(unittest.TestCase):
def test_zip(self):
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue', 'yellow']
for name, color in itertools.izip(names, colors):
print(name, color)
|
izip 返回的是 generator 。zip 返回都是 list 。
C++ 版本
1
2
3
4
5
6
7
8
9
10
11
12
| #include <catch_with_main.hpp>
#include <range/v3/all.hpp>
using namespace ranges;
TEST_CASE("zip") {
auto names = std::vector<const char*>{"raymond", "rachel", "matthew"};
auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
for(const auto& [name, color] : view::zip(names, colors)) {
std::cout << name << " " << color << std::endl;
}
}
|
sorted
Python 版本
1
2
3
4
5
6
7
| import unittest
class Test(unittest.TestCase):
def test_sort(self):
colors = ['red', 'green', 'blue', 'yellow']
for color in sorted(colors):
print(color)
|
C++ 版本
1
2
3
4
5
6
7
8
9
10
11
12
| #include <catch_with_main.hpp>
#include <range/v3/all.hpp>
using namespace ranges;
TEST_CASE("sort") {
auto colors = std::vector<std::string>{"red", "green", "blue", "yellow"};
colors |= action::sort;
for(const auto& color : colors) {
std::cout << color << std::endl;
}
}
|
这个例子里const char*换成了std::string,因为只有字符串类型才知道怎么比较,才能排序。 action::sort与 view 不同,它返回的是具体的 container ,而不再是 view 了。
如果要倒过来排序,再 python 中是这样的
1
2
3
4
5
6
7
| import unittest
class Test(unittest.TestCase):
def test_sort_reverse(self):
colors = ['red', 'green', 'blue', 'yellow']
for color in sorted(colors, reverse=True):
print(color)
|
C++ 版本
1
2
3
4
5
6
7
8
9
10
11
12
| #include <catch_with_main.hpp>
#include <range/v3/all.hpp>
using namespace ranges;
TEST_CASE("sort reverse") {
auto colors = std::vector<std::string>{"red", "green", "blue", "yellow"};
colors |= action::sort(std::greater<std::string>());
for(const auto& color : colors) {
std::cout << color << std::endl;
}
}
|
Python 还支持指定属性去排序
1
2
3
4
5
6
7
8
| import unittest
class Test(unittest.TestCase):
def test_custom_sort(self):
colors = ['red', 'green', 'blue', 'yellow']
for color in sorted(colors, key=lambda e: len(e)):
print(color)
|
C++ 版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #include <catch_with_main.hpp>
#include <range/v3/all.hpp>
using namespace ranges;
TEST_CASE("custom sort") {
auto colors = std::vector<std::string>{"red", "green", "blue", "yellow"};
colors |= action::sort(std::less<std::string>(), [](const auto&e) {
return e.size();
});
for(const auto& color : colors) {
std::cout << color << std::endl;
}
}
|
sort的第一个参数是 comparator ,第二个参数是 projector 。这里我们使用了一个 lambda 表达式,从字符串上取得其长度值,用长度去排序。
来源:https://www.v2ex.com/t/302179