Этот пост является частью моего Руководства по C ++ для разработчиков EOS.

  1. Основы
  2. Вызов по значению / ссылке и указателям
  3. Классы и структуры
  4. Шаблоны
  5. Итераторы и лямбда-выражения
  6. Мультииндекс
  7. Заголовочные файлы

Итераторы

Давайте поговорим об итераторах, действительно полезном инструменте, который активно используется во всей кодовой базе EOS. Если вы работаете с JavaScript, возможно, вы уже знакомы с итераторами, как будто они используются в for of циклах. Ключевая концепция итераторов - предоставить более удобный способ итерации по коллекции элементов. Дополнительным преимуществом является то, что вы можете реализовать интерфейс итератора для любых пользовательских классов, что делает итераторы универсальным способом просмотра данных.

// @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 ++ итераторы являются предпочтительным способом перебора коллекций элементов (vectors, lists, maps). Кроме того, ключевое слово auto избавляет вас от набора многословных типов, но может привести к менее выразительному коду.

Лямбда-выражения

Вооружившись итераторами, мы можем приступить к изучению концепций функционального программирования современного C ++. Многие функции из стандартной библиотеки принимают в качестве параметров диапазон элементов, представленных двумя итераторами (начало и конец) и анонимной функцией (лямбда-функцией). Затем эта анонимная функция применяется к каждому элементу в диапазоне. Они называются анонимными функциями, поскольку они не привязаны к переменной, а представляют собой короткие логические блоки, переданные как встроенный аргумент функции более высокого порядка. Обычно они уникальны для функции, которой они передаются, и поэтому не требуют дополнительных затрат на имя (анонимно).

С его помощью мы можем создавать конструкции, похожие на сортировку, сопоставление, фильтрацию и т. Д., Которые легко реализовать на таких языках, как 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) -> bool {return a > b; }. Обратите внимание, что -> bool определяет возвращаемое логическое значение. Часто можно не указывать тип возвращаемого значения, поскольку он может быть выведен из типа возвращаемого значения в теле функции.

Если вы хотите использовать переменные, определенные в области видимости вне вашей лямбда-функции, вам необходимо выполнить захват переменных. Снова появилась возможность передавать аргументы по ссылке или значению в вашу функцию.

  • Чтобы передать по ссылке, вам нужно начать лямбду с символа & (например, при использовании ссылок в функции): [&]
  • Для передачи по значению используйте символ =: [=]

Также существует возможность смешивать и сопоставлять захват по значению и ссылке.
Например, [=, &foo] создаст копии для всех переменных, кроме foo, который фиксируется по ссылке.

Это помогает понять, что происходит за кулисами при использовании лямбда-выражений:

Оказывается, лямбды реализуются путем создания небольшого класса; этот класс перегружает оператор (), так что он действует как функция. Лямбда-функция является экземпляром этого класса; при создании класса любые переменные в окружающей среде передаются в конструктор класса лямбда-функции и сохраняются как переменные-члены. Фактически, это очень похоже на идею функтора, которая уже возможна. Преимущество C ++ 11 заключается в том, что это становится почти тривиально простым - так что вы можете использовать его все время, а не только в очень редких случаях, когда имеет смысл написать целый новый класс. Программирование лямбда-функций

Лямбда-функции широко используются в смарт-контрактах EOS, поскольку они предоставляют действительно удобный способ изменения данных с помощью небольшого количества кода. В стандартной библиотеке есть другие функции, которые работают аналогично тому, что мы уже видели с sort, transform, remove_if и find_if. Все они экспортируются через заголовок <algorithm>.

Первоначально опубликовано на cmichel.io

Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик