C++基础:Stanford CS106L学习笔记 11 Lambdas表达式

目录

      • [11.1 函数和Lambdas表达式](#11.1 函数和Lambdas表达式)
        • [11.1.1 函数作为谓词](#11.1.1 函数作为谓词)
        • [11.1.2 Lambda函数](#11.1.2 Lambda函数)
        • [11.1.3 函子(functor)](#11.1.3 函子(functor))
      • [11.2 算法\<algorithm>](#11.2 算法<algorithm>)
      • [11.3 Ranges&View](#11.3 Ranges&View)
        • [11.3.1 ranges(c++20)](#11.3.1 ranges(c++20))
        • [11.3.2 views(c++20)](#11.3.2 views(c++20))
        • [11.3.3 Ranges&Views](#11.3.3 Ranges&Views)

11.1 函数和Lambdas表达式

11.1.1 函数作为谓词

谓词是一个布尔值函数。

加上谓词的模板

cpp 复制代码
// 一般情况
template <typename It>
It find(It first, It last, ???? pred) {
    for (auto it = first; it != last; ++it) {
        if (pred(*it)) return it;
    }
    return last;
}

// 加上谓词的模板
// Pred:我们谓词的类型。编译器会通过隐式实例化帮我们弄清楚这一点!
template <typename It, typename​ ​Pred>
// find_if函数新名字,pred:我们的谓词,作为参数传递
It find_if(It first, It last, Pred​ ​pred) {
  for (auto it = first; it != last; ++it) {
      // 我们正在对每个元素调用我们的谓词。一旦找到一个匹配的,我们就返回。
      if (pred(*it)) return it;
  }
  return last;
}

使用:

cpp 复制代码
// 案例1
bool isVowel(char c) {
  c = toupper(c);
  return c == 'A' || c == 'E' || c == 'I' ||
          c == 'O' || c == 'U';
}

std::string corlys = "Lord of the Tides";
auto it = find_if(corlys.begin(), corlys.end(), isVowel);
*it = '0'; // "L0rd of the Tides"

// 案例2
bool isPrime(size_t n) {
  if (n < 2) return false;
  for (size_t i = 3; i <= std::sqrt(n); i++)
      if (n % i == 0) return false;
  return true;
}

std::vector<int>ints = {1, 0, 6};
auto it = find_if(ints.begin(), ints.end(), isPrime);
assert(it == ints.end());

传递函数使我们能够用用户定义的行为泛化算法。

Pred是一个函数指针。

cpp 复制代码
find_if(corlys.begin(), corlys.end(), isVowel);
// Pred = bool(*)(char)    函数返回布尔类型,函数指针,接受一个char类型作为参数
find_if(ints.begin(), ints.end(), isPrime);
// Pred = bool(*)(int)

但是,函数指针的泛化性差:

cpp 复制代码
bool lessThan5(int x) { return x < 5; }
bool lessThan6(int x) { return x < 6; }
bool lessThan7(int x) { return x < 7; }

find_if(begin, end, lessThan5);
find_if(begin, end, lessThan6);
find_if(begin, end, lessThan7);
11.1.2 Lambda函数

Lambda函数是从封闭作用域捕获状态的函数。

  1. 先明确两个关键概念
  • Lambda 函数 :本质是编程语言中一种匿名、轻量级的函数(没有正式函数名,代码简洁),常见于 Python、Java、C++ 等语言,多用于临时需要一个简单函数的场景(比如排序、过滤数据)。
  • 封闭作用域:指定义 Lambda 函数的 "外部环境"------ 比如 Lambda 在一个普通函数内部定义,那么这个普通函数的作用域(包含其中的变量、参数等)就是 Lambda 的 "封闭作用域"。
  1. 核心:"捕获状态" 的含义

"捕获状态" 即 Lambda 函数能​访问并使用封闭作用域中的变量 / 数据​,而不是只能用自身参数或全局变量。 这是 Lambda 区别于 "纯局部函数" 的关键 ------ 它像 "记住了自己诞生时的环境",能把外部环境的 "状态"(变量值等)"带在身上" 使用。

cpp 复制代码
int n;
std::cin >> n;
auto lessThanN = [n](int x) { return x < n; };

find_if(begin, end, lessThanN);
cpp 复制代码
auto lambda = [capture-values](arguments) {
    return expression;
}
[x](arguments)                 // captures x by value (makes a copy)
[x&](arguments)                // captures x by reference
[x, y](arguments)        	   // captures x, y by value
[&](arguments)                 // captures everything by reference
[&, x](arguments)              // captures everything except x by reference
[=](arguments)                 // captures everything by value

Lambda函数也可以没有捕获参数。

cpp 复制代码
std::stringcorlys = "Lord of the tides";
auto it = find_if(corlys.begin(), corlys.end(), 
  [](auto c) {
    c= toupper(c);
  return c == 'A' || c == 'E' || 
  c == 'I' || c == 'O' || c == 'U';
  });

此时,编译器就会自动为auto生成模板

cpp 复制代码
auto lessThanN = [n](auto x) { 
    return x < n; 
};

// 编译器转化为:
template <typename T>
auto lessThanN = [n](T x) { 
    return x < n; 
};
11.1.3 函子(functor)

定义:函子(functor)是任何定义了operator()运算符的对象。

实例:

cpp 复制代码
template <typename T>
struct std::greater {
   bool operator()(constT& a, constT& b) const {
  	   return a >b;
   }
};

std::greater<int> g;
g(1, 2); // false
cpp 复制代码
template <>
// 这是对MyType类型的模板特化,也是为自定义类型创建哈希函数的方法之一
struct std::hash<MyType> {    
  size_toperator()(const MyType& v) const {
	  // Crazy, theoretically rigorous hash function
	  // approved by 7 PhDs and Donald Knuth goes here
	  return ...;
  }
};

MyType m;
std::hash<MyType> hash_fn;
hash_fn(m); // 125123201 (for example)

由于函子是对象,所以也有状态

cpp 复制代码
struct my_functor {
  bool operator()(inta) const {
    return a * value;
  }
  // 状态
  int​ value;
};

my_functor f;
f.value= 5;
f(10); // 50

当使用Lambda函数时,函子类型就生成了

当使用范围for时,迭代器类型就生成了


可以将一段代码转换成背后更为详细的代码。

实用网站:cpp代码的背后

当使用范围for时,迭代器类型就生成了:

同理,当使用Lambda函数时,函子类型就生成了(语法糖罢了):

std::function 是函数 /lambda 表达式的一种通用类型

  • 任何函数对象 /lambda 表达式 / 函数指针都可以被转换为该类型
  • 它的速度会稍慢一些
  • 我通常会使用 auto 模板,而不用担心类型问题!
cpp 复制代码
std::function<bool(int, int)> less = std::less<int>{};
std::function<bool(char)> vowel = isVowel;
std::function<int(int)> twice = [](int x) { return x * 2; };
  • std::function<bool(int, int)> less = std::less<int>{}:用 std::function 存储标准库中的 less 仿函数(比较两个 int 大小)
  • std::function<bool(char)> vowel = isVowel:存储自定义函数 isVowel(判断字符是否为元音)
  • std::function<int(int)> twice = [](int x) { return x * 2;}:存储 lambda 表达式(实现整数翻倍功能)

11.2 算法<algorithm>

<algorithm>是一组模板函数的集合

<algorithm> 是 C++ 标准库的核心头文件之一,其核心作用是提供​通用的算法工具集​,这些工具本质上就是通过预设逻辑实现对数据的 "检查(inspect)" 与 "转换(transform)",无需开发者重复编写底层逻辑。

11.3 Ranges&View

11.3.1 ranges(c++20)

范围(Ranges)是标准模板库(STL)的一个新版本。

范围:范围是任何具有起点和终点的事物。

cpp 复制代码
int main() {
  std::vector<char> v = {'a', 'b', 'c', 'd', 'e'};
  auto it =std::ranges::find(v, 'c');
}
cpp 复制代码
int main() {
  std::vector<char> v = {'a', 'b', 'c', 'd', 'e'};

  // Search from 'b' to 'd'
  auto first = v.begin() + 1;
  auto last = v.end() - 1;
  auto it = std::ranges::find(first, last, 'c');
}

concept约束特性:

cpp 复制代码
template<class T>
// 通过 concept 定义,要求类型 T 必须有begin()和end()方法,即能获取起始和结束迭代器
concept range = requires(T& t) { ranges::begin(t); ranges::end (t); };
template<class T>
// 是一种特殊的范围,其迭代器必须满足输入迭代器(input iterator)的要求
concept input_range =
        ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;
// 以find算法为例,它明确使用了input_range概念来约束参数,确保传入的范围符合算法的使用要求        
template<ranges::input_range R, class T, class Proj = std::identity>
borrowed_iterator_t<R> find( R&& r, const T& value, Proj proj = {} );
11.3.2 views(c++20)

视图(Views):一种组合算法的方式

视图是一个范围,它延迟地适配另一个范围。

cpp 复制代码
std::vector<char> v = {'a', 'b', 'c', 'd', 'e'};
// Filter -- Get only the vowels
std::vector<char> f;
std::copy_if(v.begin(), v.end(), std::back_inserter(f), isVowel);
// Transform -- Convert to uppercase
std::vector<char> t;
std::transform(f.begin(), f.end(), std::back_inserter(t), toupper);
// { 'A', 'E' }

/////////////////////////////////////////////
// 用view
std::vector<char> letters = {'a', 'b', 'c', 'd', 'e'};
auto f = std::ranges::views::filter(letters, isVowel);
//f 是一个视图!它接收一个底层范围 letters
// 并生成一个只包含元音的新范围!
auto t = std::ranges::views::transform(f, toupper);
//t 是一个视图!它接收一个底层范围 f
// 并生成一个包含大写字符的新范围!
auto vowelUpper = std::ranges::to<std::vector<char>>(t);

我们可以使用运算符 | 将视图链接在一起

cpp 复制代码
std::vector<char> letters = {'a','b','c','d','e'};
std::vector<char> upperVowel = letters
        | std::ranges::views::filter(isVowel)
        | std::ranges::views::transform(toupper)
        | std::ranges::to<std::vector<char>>();
// upperVowel = { 'A', 'E' }
11.3.3 Ranges&Views

ranges是立即执行的!

cpp 复制代码
// This actually sorts vec, RIGHT NOWWW!!!! 
std::ranges::sort(v);

C++20 中引入的std::ranges::views的 "惰性 (lazy)" 特性:

  1. **惰性求值 (Lazy Evaluation)**:std::ranges::views不会立即执行操作,而是在真正需要结果时才会计算。这与立即执行的算法形成对比,后者会马上处理数据并生成新容器。
  2. 代码解析
  • letters | views::filter(isVowel) | views::transform(toupper) 只是创建了一个 "视图",定义了要执行的操作序列,但并未实际执行。
  • 只有当调用std::ranges::to<std::vector<char>>(view)时,才会真正执行过滤和转换操作,生成包含大写元音字母的向量。

你可能会喜欢范围 / 视图的原因?

✅ 少担心迭代器

✅ 受约束的算法意味着更好的错误消息

✅ 超级易读的函数式语法

你可能会不喜欢范围 / 视图的原因?

❌ 它们非常新,尚未完全具备所有功能

❌ 缺乏编译器支持

❌ 与手工编写的版本相比性能有所下降

相关推荐
菜鸟‍2 小时前
【课程学习】
学习·信息与通信
断剑zou天涯2 小时前
【算法笔记】资源限制类题目的解题套路
笔记·算法·哈希算法
lxh01132 小时前
2025/12/19学习记录
学习
辞旧 lekkk2 小时前
【c++】c++11(上)
开发语言·c++·学习·萌新
彭世瑜2 小时前
C/C++:libfort用于在终端输出表格
c语言·开发语言·c++
Dream it possible!2 小时前
LeetCode 面试经典 150_回溯_全排列(100_46_C++_中等)
c++·leetcode·面试·回溯
走在路上的菜鸟2 小时前
Android学Dart学习笔记第二十一节 类-点的简写
android·笔记·学习·flutter
黑客思维者2 小时前
机器学习009:监督学习【回归算法】(岭回归)-- 给模型一个“清醒”的约束
学习·机器学习·回归·监督学习·岭回归
深蓝海拓2 小时前
PySide6从0开始学习的笔记(十一) QSS 属性选择器
笔记·python·qt·学习·pyqt