C++开发EOS基础指南:迭代器和Lambda表达式

让我们来谈谈迭代器,它是一个非常有用的工具,在整个EOS代码库中大量使用。如果您来自JavaScript背景,您可能已经熟悉迭代器,就像它们用于循环一样。迭代器的关键概念是提供一种更好的方法来遍历项集合。额外的好处是您可以为任何自定义类实现迭代器接口,使迭代器成为遍历数据的通用方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// @url: https://repl.it/@MrToph/CPPBasics-Iterators
#include <iostream>
#include <vector>

using namespace std;

int main()
{
vector<int> v{2, 3, 5, 8};
// old way to iterate
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << "\n";
}

// using Iterators
// begin() returns an iterator that points to the beginning of the vector
// end() points to the end, can be compared using != operator
// iterators are incremented by using the + operator thanks to operator-overloading
for (vector<int>::iterator i = v.begin(); i != v.end(); i++)
{
// iterators are dereferenced by * like pointers
// returns the element the iterator is currently pointing to
cout << *i << "\n";
}

// auto keyword allows you to not write the type yourself
// instead C++ infers it from the return type of v.begin
for (auto i = v.begin(); i != v.end(); i++)
{
cout << *i << "\n";
}

// can use arithmetic to "jump" to certain elements
int thirdElement = *(v.begin() + 2);
cout << "Third: " << thirdElement << "\n";
// end is the iterator that points to the "past-the-end" element
// The past-the-end element is the theoretical element that would follow the last element in the vector.
// It does not point to any element, and thus shall not be dereferenced.
int lastElement = *(v.end() - 1);
cout << "Last: " << lastElement << "\n";

// do not go out of bounds by iterating past the end() iterator
// the behavior is undefined
// BAD: v.end() + 1, v.begin() + 10
}

在现代C++中,迭代器是迭代元素集合(向量,列表,映射)的首选方式。另外,auto关键字可以避免输入word类型,但可能会导致代码性能降低。

Lambda表达式

使用迭代器,我们可以开始研究现代C++的函数式编程概念。标准库中的许多函数采用由两个迭代器(开始和结束)和匿名函数(lambda函数)表示的一系列元素作为参数。然后将此匿名函数应用于范围内的每个元素。它们被称为匿名函数,因为它们不是绑定到变量,而是它们是短逻辑块,作为内联参数传递给更高阶函数。通常,它们对于传递给它们的函数是唯一的,因此不需要具有名称(匿名)的整个开销。

有了它,我们可以实现类似于排序,映射,过滤等的构造,这些构造在JavaScript等语言中很容易实现:

1
[1,2,3,4].map(x => x*x).filter(x => x % 2 === 1).sort((a,b) => b - a)

C++中的代码并不简洁,但结构相同。来自std库的许多函数式编程助手以半开间隔运行,这意味着包括较低范围,排除较高范围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// @url: https://repl.it/@MrToph/CPPBasics-Lambdas
#include <iostream>
#include <vector>
// for sort, map, etc.
#include <algorithm>

using namespace std;

int main()
{
vector<int> v{2, 1, 4, 3, 6, 5};
// first two arguments are the range
// v.begin() is included up until v.end() (excluded)
// sorts ascending
sort(v.begin(), v.end());

// in C++, functions like sort mutate the container (in contrast to immutability and returning new arrays in other languages)
for (auto i = v.begin(); i != v.end(); i++)
{
cout << *i << "\n";
}

// sort it again in descending order
// third argument is a lambda function which is used as the comparison for the sort
sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

// functional for_each, can also use auto for type
for_each(v.begin(), v.end(), [](int a) { cout << a << "\n"; });

vector<string> names{"Alice", "Bob", "Eve"};
vector<string> greetings(names.size());

// transform is like a map in JavaScript
// it applies a function to each element of a container
// and writes the result to (possibly the same) container
// first two arguments are range to iterate over
// third argument is the beginning of where to write to
transform(names.begin(), names.end(), greetings.begin(), [](const string &name) {
return "Hello " + name + "\n";
});
// filter greetings by length of greeting
auto new_end = std::remove_if(greetings.begin(), greetings.end(), [](const string &g) {
return g.size() > 10;
});
// iterate up to the new filtered length
for_each(greetings.begin(), new_end, [](const string &g) { cout << g; });
// alternatively, really erase the filtered out elements from vector
// so greetings.end() is the same as new_end
// greetings.erase(new_end, greetings.end());

// let's find Bob
string search_name = "Bob";
// we can use the search_name variable defined outside of the lambda scope
// notice the [&] instead of [] which means that we want to do "variable capturing"
// i.e. make all local variables available to use in the lambda function
auto bob = find_if(names.begin(), names.end(), [&](const string &name) {
return name == search_name;
});
// find_if returns an iterator referncing the found object or the past-the-end iterator if nothing was found
if (bob != names.end())
cout << "Found name " << *bob << "\n";
}

匿名函数的语法是在C++中习惯的东西。它们由括号指定,后跟参数列表,如[](int a,int b) - > bool {return a> b; }。请注意,- > bool指定一个布尔返回值。通常,您可以避免表达返回类型,因为它可以从函数体中的返回类型推断出来。

如果要使用lambda函数之外的作用域中定义的变量,则需要进行变量捕获。还有可能通过引用或值将参数传递给您的函数。

  • 要通过引用传递,您需要使用字符启动lambda(就像在函数中使用引用时一样):[&]
  • 要传递值,请使用=字符:[=]

还可以通过值和参考来混合和匹配捕获。

例如,[=,&foo]将为除foo之外的所有变量创建副本,这些变量通过引用捕获。

它有助于理解使用lambdas时幕后发生的事情:

事实证明,lambdas的实现方式是创建一个小类;这个类重载了operator(),因此它就像一个函数一样。lambda函数是这个类的一个实例;构造类时,周围环境中的任何变量都将传递给lambda函数类的构造函数并保存为成员变量。事实上,这有点像已经可能的仿函数的想法。C++ 11的好处是,这样做变得非常简单——所以你可以一直使用它,而不是仅仅在非常罕见的情况下编写一个全新的类是有意义的。

Lambda函数在EOS智能合约中大量使用,因为它们提供了一种非常方便的方法来修改少量代码中的数据。标准库中有更多函数,它们与我们在sorttransformremove_iffind_if中看到的函数类似。它们都通过<algorithm>标头导出。

======================================================================

分享一些比特币、以太坊、EOS等区块链相关的交互式在线编程实战教程:

汇智网原创翻译,转载请标明出处。这里是C++开发EOS基础指南:迭代器和Lambda表达式


文章标签:

原文连接:http://blog.hubwiz.com/2019/04/24/eos-c-lambda/

相关推荐

随机数索引(一题双解)【Leetcode每日(4.25)一题】C++

坚持用C++刷牛客题(剑指offer专题)

C语言——三种方式实现学生信息管理

深入详解C/C++动态内存管理

【C】文件操作进阶知识

【c ++ primer 笔记】第8章 IO库

【C++】多态(C++重中之重)(学习与复习兼顾)

【OpenCV】图像拼接 原理介绍 C++ OpenCV 案例实现

算法日志——第二天

C语言文件篇——文件操作

【C++】从设计原理来看string类

C++学习记录1

洛谷每日三题之第六天

【C】柔性数组

java与c++的区别

【27. 表达式求值(中缀表达式)】

CMU15445 (Fall 2019) 之 Project#4 - Logging & Recovery 详解

C/C++牛客网刷题练习之翻转链表篇

C++代码和可执行程序在x86和arm上的区别

生命游戏(信息学堂2022)