c++基础(lambda)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++基础(lambda)相关的知识,希望对你有一定的参考价值。

参考技术A 在 c++ 中 lambda 函数有点类似匿名函数,怎么解释呢,我们无需定义一个函数结构随后再去使用这个函数。而是直接在使用时将其声明使用。

再进一步解释可以将函数赋值给变量,然后通过变量再去调用函数。我们学习一个新特性不是如何使用这个语法,而是在什么场景下恰当地去使用这个语法来解决实际问题。不要为了特效而改变故事情节,一个好的程序在其情节而不是特效。所以不要为了用而用。
lambda 是字面上定义一个函数而不是持有一个函数。也就是我们可以使用函数指针的场景是可以使用 lambda 来代替的。参照 c++ 基础(函数指针)

定义 void ForEach(const std::vector<int> &values, void (*func)(int)) 接收两个参数一个 vector<int> 动态数组,一个是指针函数作为参数。在调用函数 ForEach 时候第二个参数我们传入一个 [](int value) std::cout << "value: " << value << std::endl; 一个 lambda 作为第二参数传入到函数 ForEach 中。 void (*func)(int) 定义传入 lambda 的格式是返回值为 void 接收一个 int 类型的参数。
在循环体中的 func(value) 会调用 lambda 函数。

大家感觉陌生和疑惑可能是 [] ,今天对这个解释一下,看了一些有关 [] 解释的参考资料,感觉和之前学习的 angularjs 中有关 directive 变量定义内部是对外的引用、还是复制的=,& 符号含义有点相似,这些符号主要传入到 directive 的变量是传值还是传引用。可能是 angularjs 设计者参考c++的做法了吧。

在 c++ 中 [] 为 lambda 的捕获区,用于捕获外部变量,我们可以看一看。

这里我们需要修改第二个参数传入的类型 const std::function<void(int)> &func 。

如果想要在 lambda 表达式中修改传入的变量 a ,就需要使用 lambda 表达式中添加 一个 mutable 的说明符。

下面看一个 lambda 的具体应用,在 algorithm 库中提供了find_if 函数最后一个参数接收函数作为参数,我们可以传入一个 lambda 表达式来作为参数。

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

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

// @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,2,3,4].map(x => x*x).filter(x => x % 2 === 1).sort((a,b) => b - a)

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

// @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) - &gt; bool {return a&gt; b; }。请注意,- &gt; bool指定一个布尔返回值。通常,您可以避免表达返回类型,因为它可以从函数体中的返回类型推断出来。

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

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

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

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

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

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

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

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

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

  • EOS入门教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • 深入浅出玩转EOS钱包开发,本课程以手机EOS钱包的完整开发过程为主线,深入学习EOS区块链应用开发,课程内容即涵盖账户、计算资源、智能合约、动作与交易等EOS区块链的核心概念,同时也讲解如何使用eosjs和eosjs-ecc开发包访问EOS区块链,以及如何在React前端应用中集成对EOS区块链的支持。课程内容深入浅出,非常适合前端工程师深入学习EOS区块链应用开发。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • c#比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在C#代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是C#工程师不可多得的比特币开发学习课程。
  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • ERC721以太坊通证实战,课程以一个数字艺术品创作与分享DApp的实战开发为主线,深入讲解以太坊非同质化通证的概念、标准与开发方案。内容包含ERC-721标准的自主实现,讲解OpenZeppelin合约代码库二次开发,实战项目采用Truffle,IPFS,实现了通证以及去中心化的通证交易所。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • Hyperledger Fabric 区块链开发详解,本课程面向初学者,内容即包含Hyperledger Fabric的×××书与MSP服务、权限策略、通道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、nodejs链码与应用开发的操作实践,是Nodejs工程师学习Fabric区块链开发的最佳选择。
  • Hyperledger Fabric java 区块链开发详解,课程面向初学者,内容即包含Hyperledger Fabric的×××书与MSP服务、权限策略、频道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、java链码与应用开发的操作实践,是java工程师学习Fabric区块链开发的最佳选择。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

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

以上是关于c++基础(lambda)的主要内容,如果未能解决你的问题,请参考以下文章

虚幻4与现代C++:Lambda好用也有坑

虚幻4与现代C++:Lambda好用也有坑

转载C++ functionbind和lambda表达式

C/C++: C++可调用对象详解

蓝桥ROS机器人之现代C++学习笔记3.1 Lambda 表达式

python基础