C++学习:六个月从基础到就业——C++11/14:lambda表达式
C++学习:六个月从基础到就业——C++11/14:lambda表达式
本文是我C++学习之旅系列的第四十篇技术文章,也是第三阶段"现代C++特性"的第二篇,主要介绍C++11/14中引入的lambda表达式。查看完整系列目录了解更多内容。
引言
Lambda表达式是C++11引入的一项重要特性,它允许我们在需要函数对象的地方直接定义内联匿名函数,无需显式定义一个函数或函数对象类。这大大简化了代码,特别是在使用STL算法和回调函数的场景中。C++14进一步扩展了lambda的功能,使其更加灵活和强大。本文将深入探讨lambda表达式的语法、使用方法及实际应用,帮助你充分利用这一强大特性。
Lambda表达式基础
基本语法
Lambda表达式的基本语法如下:
[capture_list](parameters) mutable noexcept -> return_type { body }
其中:
[capture_list]
:捕获外部变量的列表(可为空[]
)(parameters)
:参数列表(可为空()
,与普通函数一样)mutable
:可选,允许修改按值捕获的变量noexcept
:可选,指明函数不抛出异常-> return_type
:可选,指定返回类型(C++11中有时需要,C++14中往往可以省略){ body }
:函数体
最简单的lambda表达式例子:
#include <iostream>int main() {// 没有参数的lambdaauto sayHello = []() { std::cout << "Hello, Lambda!" << std::endl; };sayHello(); // 输出:Hello, Lambda!// 带参数的lambdaauto add = [](int a, int b) { return a + b; };std::cout << "5 + 3 = " << add(5, 3) << std::endl; // 输出:5 + 3 = 8// 使用auto参数(C++14)auto multiply = [](auto a, auto b) { return a * b; };std::cout << "5 * 3 = " << multiply(5, 3) << std::endl; // 整数相乘std::cout << "5.5 * 3.5 = " << multiply(5.5, 3.5) << std::endl; // 浮点数相乘return 0;
}
捕获列表详解
捕获列表指定了lambda可以访问的外部作用域中的变量:
1. 值捕获
使用值捕获时,lambda在创建时获取变量的副本:
#include <iostream>int main() {int x = 10;// 按值捕获xauto lambda = [x]() { std::cout << "Captured x: " << x << std::endl; };x = 20; // 修改原始变量lambda(); // 输出:Captured x: 10,因为lambda捕获的是创建时的副本return 0;
}
2. 引用捕获
使用引用捕获时,lambda可以访问并修改原始变量:
#include <iostream>int main() {int x = 10;// 按引用捕获xauto lambda = [&x]() {std::cout << "Before modification x: " << x << std::endl;x = 30; // 修改原始变量std::cout << "After modification x: " << x << std::endl;};lambda(); // 修改xstd::cout << "x in main(): " << x << std::endl; // 显示修改后的值:30return 0;
}
3. 隐式捕获
使用[=]
或[&]
可以隐式捕获所有使用的变量:
#include <iostream>int main() {int x = 10;int y = 20;// 隐式按值捕获所有变量auto lambda1 = [=]() { std::cout << "Captured x: " << x << ", y: " << y << std::endl; };// 隐式按引用捕获所有变量auto lambda2 = [&]() {x = 30;y = 40;std::cout << "Modified x: " << x << ", y: " << y << std::endl;};lambda1(); // 输出:Captured x: 10, y: 20lambda2(); // 修改原始变量并输出:Modified x: 30, y: 40std::cout << "After lambda2: x = " << x << ", y = " << y << std::endl; // 显示修改后的值return 0;
}
4. 混合捕获
可以混合使用显式和隐式捕获:
// 隐式按值捕获所有变量,但x按引用捕获
auto lambda = [=, &x]() { x = y + z; // 可以修改x,但不能修改y和z
};// 隐式按引用捕获所有变量,但x按值捕获
auto lambda = [&, x]() { y = x + z; // 可以修改y和z,但不能修改x
};
5. 初始化捕获(C++14)
C++14允许在捕获列表中初始化变量:
#include <iostream>
#include <memory>int main() {// 初始化捕获auto ptr = std::make_unique<int>(10);// 在捕获列表中移动unique_ptr的所有权auto lambda = [value = std::move(ptr)]() {if (value) {std::cout << "Captured value: " << *value << std::endl;}};// ptr现在为nullptrstd::cout << "ptr is " << (ptr ? "not null" : "null") << std::endl;lambda(); // 输出:Captured value: 10return 0;
}
mutable关键字
默认情况下,lambda表达式无法修改按值捕获的变量。使用mutable
关键字可以解除这个限制:
#include <iostream>int main() {int x = 10;// 没有mutable,尝试修改x会导致编译错误// auto lambda1 = [x]() { x = 20; }; // 编译错误// 使用mutable允许修改值捕获的变量auto lambda2 = [x]() mutable {x = 20; // 可以修改捕获的副本,但不影响外部原始变量std::cout << "Inside lambda: x = " << x << std::endl;};lambda2(); // 输出:Inside lambda: x = 20std::cout << "Outside lambda: x = " << x << std::endl; // 输出:Outside lambda: x = 10return 0;
}
返回类型推导
在C++11中,当lambda的函数体包含单一return语句时,返回类型可以自动推导。在其他情况下,需要显式指定返回类型:
#include <iostream>int main() {// 返回类型自动推导为intauto add = [](int a, int b) { return a + b; };// 复杂情况需要显式指定返回类型(C++11)auto getValueC11 = [](bool condition) -> int {if (condition) {return 42;} else {return 0;}};std::cout << "Add result: " << add(5, 3) << std::endl;std::cout << "True condition: " << getValueC11(true) << std::endl;std::cout << "False condition: " << getValueC11(false) << std::endl;return 0;
}
C++14增强了返回类型推导,即使在复杂情况下也能自动推导返回类型:
#include <iostream>
#include <string>int main() {// C++14中返回类型自动推导,即使有多个return语句auto getValueC14 = [](bool condition) {if (condition) {return 42;} else {return 0;}};// 不同类型的返回值也可以推导(根据上下文转换)auto convertC14 = [](bool condition) {if (condition) {return 42; // int} else {return 42.0; // double}}; // 返回类型被推导为doublestd::cout << "Type of convertC14 result: " << typeid(convertC14(true)).name() << std::endl;return 0;
}
Lambda表达式的进阶特性
泛型Lambda(C++14)
C++14引入了泛型lambda,允许在参数中使用auto
关键字:
#include <iostream>
#include <vector>
#include <string>int main() {// 泛型lambda,可以接受任何类型的参数auto print = [](const auto& value) {std::cout << "Value: " << value << std::endl;};print(42); // 整数print(3.14159); // 浮点数print("Hello"); // 字符串字面量print(std::string("World")); // std::string对象// 处理不同容器auto sumElements = [](const auto& container) {typename std::decay<decltype(container)>::type::value_type sum{};for (const auto& elem : container) {sum += elem;}return sum;};std::vector<int> intVec = {1, 2, 3, 4, 5};std::vector<double> doubleVec = {1.1, 2.2, 3.3, 4.4, 5.5};std::cout << "Sum of integers: " << sumElements(intVec) << std::endl;std::cout << "Sum of doubles: " << sumElements(doubleVec) << std::endl;return 0;
}
Lambda表达式的类型
每个lambda表达式都有唯一的闭包类型,该类型只有编译器知道。我们通常使用auto
来存储lambda:
auto lambda = []() { std::cout << "Hello" << std::endl; };
如果需要存储具有相同签名的不同lambda,可以使用std::function
:
#include <iostream>
#include <functional>
#include <vector>int main() {// 使用std::function存储lambdastd::function<int(int, int)> operation;bool use_addition = true;if (use_addition) {operation = [](int a, int b) { return a + b; };} else {operation = [](int a, int b) { return a * b; };}std::cout << "Result: " << operation(5, 3) << std::endl; // 输出:Result: 8// 存储多个相同签名的lambdastd::vector<std::function<int(int)>> transformations;transformations.push_back([](int x) { return x * x; }); // 平方transformations.push_back([](int x) { return x + x; }); // 加倍transformations.push_back([](int x) { return x * x * x; }); // 立方int value = 5;for (const auto& transform : transformations) {std::cout << "Transformed: " << transform(value) << std::endl;}return 0;
}
递归Lambda
Lambda表达式也可以递归调用自身,但需要一些技巧:
#include <iostream>
#include <functional>int main() {// 使用std::function和引用捕获实现递归std::function<int(int)> factorial;factorial = [&factorial](int n) {return (n <= 1) ? 1 : n * factorial(n - 1);};std::cout << "Factorial of 5: " << factorial(5) << std::endl; // 输出:120// C++14中的另一种方法:使用Y-combinator技巧auto Y = [](auto lambda) {return [=](auto... args) {return lambda(lambda, args...);};};auto factorial_y = Y([](auto self, int n) -> int {return (n <= 1) ? 1 : n * self(self, n - 1);});std::cout << "Y-combinator factorial of 5: " << factorial_y(5) << std::endl; // 输出:120return 0;
}
实际应用示例
与STL算法结合使用
Lambda表达式与STL算法结合使用是最常见、最有用的场景之一:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 使用lambda过滤元素auto evenCount = std::count_if(numbers.begin(), numbers.end(), [](int n) { return n % 2 == 0; });std::cout << "Number of even elements: " << evenCount << std::endl;// 使用lambda转换元素std::transform(numbers.begin(), numbers.end(), numbers.begin(),[](int n) { return n * n; });std::cout << "After squaring: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 自定义排序std::vector<std::pair<std::string, int>> people = {{"Alice", 25},{"Bob", 30},{"Charlie", 20},{"David", 35}};// 按年龄排序std::sort(people.begin(), people.end(), [](const auto& a, const auto& b) { return a.second < b.second; });std::cout << "People sorted by age:" << std::endl;for (const auto& person : people) {std::cout << person.first << ": " << person.second << std::endl;}// 使用lambda进行累加auto sum = std::accumulate(numbers.begin(), numbers.end(), 0,[](int total, int value) { return total + value; });std::cout << "Sum of squares: " << sum << std::endl;return 0;
}
事件处理与回调函数
Lambda表达式非常适合用作回调函数:
#include <iostream>
#include <functional>
#include <vector>class Button {
private:std::string name;std::function<void()> clickHandler;public:Button(const std::string& n) : name(n) {}void setClickHandler(std::function<void()> handler) {clickHandler = handler;}void click() {std::cout << "Button '" << name << "' clicked" << std::endl;if (clickHandler) {clickHandler();}}
};class EventSystem {
private:std::vector<std::function<void(const std::string&)>> eventListeners;public:void addEventListener(std::function<void(const std::string&)> listener) {eventListeners.push_back(listener);}void triggerEvent(const std::string& eventName) {std::cout << "Event '" << eventName << "' triggered" << std::endl;for (const auto& listener : eventListeners) {listener(eventName);}}
};int main() {// 按钮回调示例Button saveButton("Save");Button cancelButton("Cancel");int saveCount = 0;saveButton.setClickHandler([&saveCount]() {std::cout << "Saving data..." << std::endl;++saveCount;std::cout << "Data has been saved " << saveCount << " times" << std::endl;});cancelButton.setClickHandler([]() {std::cout << "Operation cancelled" << std::endl;});saveButton.click();cancelButton.click();saveButton.click();// 事件系统示例EventSystem events;// 添加监听器events.addEventListener([](const std::string& event) {std::cout << "Listener 1 received: " << event << std::endl;});events.addEventListener([](const std::string& event) {std::cout << "Listener 2 received: " << event << std::endl;});// 触发事件events.triggerEvent("application_start");events.triggerEvent("user_login");return 0;
}
自定义迭代器和生成器
Lambda表达式可以用于创建自定义迭代器和生成器:
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>// 简单的整数序列生成器
std::function<int()> makeIntGenerator(int start, int step) {return [start, step]() mutable {int current = start;start += step;return current;};
}// 斐波那契序列生成器
std::function<int()> makeFibonacciGenerator() {return [a = 0, b = 1]() mutable {int current = a;int next_val = a + b;a = b;b = next_val;return current;};
}int main() {// 使用整数生成器auto intGen = makeIntGenerator(1, 2); // 生成1, 3, 5, 7, ...std::cout << "Generated integers: ";for (int i = 0; i < 5; ++i) {std::cout << intGen() << " ";}std::cout << std::endl;// 使用斐波那契生成器auto fibGen = makeFibonacciGenerator(); // 生成0, 1, 1, 2, 3, 5, ...std::cout << "Fibonacci sequence: ";for (int i = 0; i < 10; ++i) {std::cout << fibGen() << " ";}std::cout << std::endl;// 生成器与STL算法结合std::vector<int> numbers(10);auto gen = makeIntGenerator(0, 5); // 生成0, 5, 10, 15, ...std::generate(numbers.begin(), numbers.end(), gen);std::cout << "Generated vector: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;return 0;
}
IIFE (立即调用的函数表达式)
Lambda表达式可以实现JavaScript中流行的IIFE模式:
#include <iostream>
#include <vector>int main() {// 普通变量初始化int result = 0;for (int i = 1; i <= 10; ++i) {result += i;}std::cout << "Sum: " << result << std::endl;// 使用IIFE初始化int sum = [](int n) {int total = 0;for (int i = 1; i <= n; ++i) {total += i;}return total;}(10);std::cout << "Sum using IIFE: " << sum << std::endl;// 复杂对象初始化std::vector<int> primes = []{std::vector<int> p;p.push_back(2);p.push_back(3);p.push_back(5);p.push_back(7);p.push_back(11);return p;}();std::cout << "Prime numbers: ";for (int prime : primes) {std::cout << prime << " ";}std::cout << std::endl;return 0;
}
C++14中的Lambda增强
C++14对lambda表达式进行了几项重要增强:
1. 泛型Lambda
如前所述,C++14引入了泛型lambda,允许在参数中使用auto
关键字。
2. 初始化捕获
C++14允许在捕获列表中初始化新变量:
#include <iostream>
#include <memory>
#include <utility>int main() {std::string message = "Hello";// C++11必须这样写auto lambda1 = [message = message + " World!"]() {std::cout << message << std::endl;};// 移动构造情况auto resource = std::make_unique<int>(42);// 在C++11中无法捕获unique_ptrauto lambda2 = [resource = std::move(resource)]() {std::cout << "Resource value: " << *resource << std::endl;};lambda1(); // 输出:Hello World!lambda2(); // 输出:Resource value: 42// 原始resource现在是nullptrstd::cout << "Original resource is " << (resource ? "valid" : "nullptr") << std::endl;return 0;
}
3. 返回类型推导改进
C++14中,编译器可以从lambda体中推导出返回类型,即使函数体包含多个返回语句。
Lambda表达式的性能考量
Lambda表达式通常被编译为内联函数对象,性能与手写函数对象相当。一些注意事项:
-
捕获的开销:
- 值捕获会创建变量的副本,可能有额外开销
- 引用捕获几乎没有额外开销
-
内联优化:
- 简单的lambda通常会被内联,没有函数调用开销
- 但过大的lambda可能不会被内联
-
std::function的开销:
std::function
比直接使用lambda有更多开销- 当需要多态行为时才使用
std::function
#include <iostream>
#include <chrono>
#include <functional>
#include <vector>// 时间测量辅助函数
template<typename Func>
long long measureTime(Func func, int iterations) {auto start = std::chrono::high_resolution_clock::now();for (int i = 0; i < iterations; ++i) {func(i);}auto end = std::chrono::high_resolution_clock::now();return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
}int main() {const int iterations = 10000000;// 测试直接使用lambdaauto directLambda = [](int x) { return x * x; };// 测试通过std::function存储的lambdastd::function<int(int)> funcLambda = [](int x) { return x * x; };// 测试值捕获int multiplier = 2;auto valueLambda = [multiplier](int x) { return x * multiplier; };// 测试引用捕获auto refLambda = [&multiplier](int x) { return x * multiplier; };// 测试性能auto directTime = measureTime([&](int i) { directLambda(i); }, iterations);auto funcTime = measureTime([&](int i) { funcLambda(i); }, iterations);auto valueTime = measureTime([&](int i) { valueLambda(i); }, iterations);auto refTime = measureTime([&](int i) { refLambda(i); }, iterations);std::cout << "Direct lambda: " << directTime << " ns" << std::endl;std::cout << "std::function lambda: " << funcTime << " ns" << std::endl;std::cout << "Value capture lambda: " << valueTime << " ns" << std::endl;std::cout << "Reference capture lambda: " << refTime << " ns" << std::endl;return 0;
}
最佳实践与注意事项
何时使用Lambda
- 简短的一次性函数:特别是作为算法参数
- 需要捕获局部变量的函数:当需要访问作用域中的变量
- 回调函数:事件处理或异步操作的回调
- 在本地定义辅助函数:不需要在全局或类范围内可见的函数
避免常见错误
-
捕获的生命周期问题:
std::function<int()> createLambda() {int local = 42;// 危险!返回的lambda捕获了即将销毁的局部变量的引用return [&local]() { return local; }; // 引用已销毁的变量 }// 安全版本:按值捕获 std::function<int()> createSafeLambda() {int local = 42;return [local]() { return local; }; // 复制了local的值 }
-
捕获this指针:
class Widget { private:int value = 42;public:// 危险,隐式捕获this可能导致悬挂指针auto badClosure() {return [=]() { return value; }; // 隐式捕获this}// C++14安全版本:显式捕获成员变量auto goodClosureC14() {return [value = value]() { return value; };}// C++11安全版本:显式复制需要的数据auto goodClosureC11() {int v = value;return [v]() { return v; };} };
-
按值捕获且修改:
int counter = 0;// 错误:无法修改按值捕获的变量 // auto increment = [counter]() { ++counter; }; // 编译错误// 正确:使用mutable关键字 auto increment = [counter]() mutable { ++counter; return counter; };
提高Lambda可读性
- 保持简短:长函数应提取为命名函数
- 使用适当的捕获方式:明确指定需要捕获的变量
- 考虑命名lambda:对于复杂lambda,使用auto给它一个有意义的名字
- 适当添加注释:特别是对于复杂的逻辑
// 不要这样做:过于复杂的lambda
std::sort(employees.begin(), employees.end(), [](const Employee& a, const Employee& b) {if (a.department != b.department)return a.department < b.department;if (a.salary != b.salary)return a.salary > b.salary; // 注意:薪水是降序排列return a.name < b.name;});// 更好的做法:命名lambda提高可读性
auto compareEmployees = [](const Employee& a, const Employee& b) {// 首先按部门升序排序if (a.department != b.department)return a.department < b.department;// 然后在同一部门内按薪水降序排序if (a.salary != b.salary)return a.salary > b.salary;// 最后按姓名字母顺序排序return a.name < b.name;
};std::sort(employees.begin(), employees.end(), compareEmployees);
总结
Lambda表达式是C++11/14引入的最强大、最有用的特性之一,它极大地简化了代码,使C++编程更加灵活和表达力更强。主要优势包括:
- 简化代码:无需定义单独的函数或函数对象类
- 局部范围:能够访问当前作用域的变量
- 即时定义:在需要使用的地方直接定义
- 提高可读性:使代码意图更加清晰
- 增强表达力:特别是与STL算法结合使用时
C++14通过泛型lambda、初始化捕获和改进的返回类型推导进一步增强了lambda表达式的功能。掌握lambda表达式是现代C++编程的必备技能,它能够帮助你编写更简洁、更易维护的代码。
在下一篇文章中,我们将探讨C++11/14的另一个重要特性:auto
类型推导,它如何简化变量声明并改善代码可读性。
这是我C++学习之旅系列的第四十篇技术文章。查看完整系列目录了解更多内容。
相关文章:
C++学习:六个月从基础到就业——C++11/14:lambda表达式
C学习:六个月从基础到就业——C11/14:lambda表达式 本文是我C学习之旅系列的第四十篇技术文章,也是第三阶段"现代C特性"的第二篇,主要介绍C11/14中引入的lambda表达式。查看完整系列目录了解更多内容。 引言 Lambda表达…...
cesium基础设置
在上节新建的程序中,我们会看到有一行小字: 原因为我们没有输入token,想要让这行小字消失的方法很简单,前往cesium的官网注册账号申请token.然后在App.vue中如下方式添加token 保存后即可发现小字消失. 如果连logo都想去掉呢? 在源代码中,我们初始化了一个viwer,即查看器窗口…...
一些好玩的东西
🚀 终极挑战:用 curl 玩《星球大战》 telnet towel.blinkenlights.nl # 其实不是 curl,但太经典了! 效果:在终端播放 ASCII 版《星球大战》电影!(如果 telnet 不可用,可以试…...
ActiveMQ 与其他 MQ 的对比分析:Kafka/RocketMQ 的选型参考(二)
ActiveMQ、Kafka 和 RocketMQ 详细对比 性能对比 在性能方面,Kafka 和 RocketMQ 通常在高吞吐量场景下表现出色,而 ActiveMQ 则相对较弱。根据相关测试数据表明,Kafka 在处理大规模日志数据时,单机吞吐量可以达到每秒数十万条甚…...
HTML学习笔记(7)
一、什么是jQuery jQuery 是一个 JavaScript 库。他实现了JavaScript的一些功能,并封装起来,对外提供接口。 例子实现一个点击消失的功能,用JavaScript实现 <!DOCTYPE html> <html lang"en"> <head><meta …...
Jenkis安装、配置及账号权限分配保姆级教程
Jenkis安装、配置及账号权限分配保姆级教程 安装Jenkins下载Jenkins启动Jenkins配置Jenkins入门Jenkins配置配置中文配置前端自动化任务流新建任务拉取代码打包上传云服务并运行配置后端自动化任务流新建任务拉取代码打包上传云服务并运行账号权限分配创建用户分配视图权限安装…...
面向对象编程(Object-Oriented Programming, OOP)是什么?
李升伟 编译 简介 如果你已经接触过软件开发领域的话,你肯定听说过"面向对象编程"(Object-Oriented Programming, OOP)这个术语。但你知道什么是OOP吗?为什么它如此重要?在这篇文章中我们将深入解析OOP的基…...
Hotspot分析(1):单细胞转录组识别信息基因(和基因模块)
这一期我们介绍一个常见的,高分文章引用很高的一个单细胞转录组分析工具Hotspot,它可针对单细胞转录组数据识别有意义基因或者基因module,类似于聚类模块。所谓的”informative "的基因是那些在给定度量中相邻的细胞之间以相似的方式表达…...
从图文到声纹:DeepSeek 多模态技术的深度解析与实战应用
目录 一、引言二、DeepSeek 技术基础2.1 架构与原理2.2 多模态能力概述 三、文本与图像关联应用3.1 图文跨模态对齐技术3.1.1 技术原理3.1.2 DeepSeek 的独特方法 3.2 图像生成与文本描述3.2.1 应用案例3.2.2 技术实现 3.3 多模态检索系统中的应用3.3.1 系统搭建流程3.3.2 实际…...
cuDNN 9.9.0 便捷安装-Windows
#工作记录 从 CUDA12.6.3 和 cuDNN9.6.0 版本起,开启了使用 exe 安装包直接进行安装升级的支持模式,彻底改变了以往那种繁琐的安装流程。 在这两个版本之前,开发者在安装 CUDA 和 cuDNN 时,不得不手动下载 cuDNN 压缩包…...
profile软件开发中的性能剖析与内存分析
在软件开发中,“Profile”(性能剖析/性能分析)指的是通过工具详细监控程序运行时的各种性能指标,帮助开发者定位代码中的效率瓶颈或资源问题。当有人建议你 “profile 一下内存问题” 时,本质上是让你用专业工具动态分…...
0.0973585?探究ts_rank的score为什么这么低
最近在使用postgres利用ts_rank进行排序找到最符合关键词要求得内容时发现: 即使是相似的内容,得分也是非常非常得低(其中一个case是0.0973585)。看起来很奇怪,非常不可行。于是我又做了一个简单测的测试: SELECT ts_rank(to_tsvector(english, skirt), to_tsquery(skirt)…...
架构思维:利用全量缓存架构构建毫秒级的读服务
文章目录 一、引言二、全量缓存架构概述三、基于 Binlog 的缓存同步方案1. Binlog 原理2. 同步中间件3. 架构整合核心收益 四、Binlog 全量缓存的优缺点与优化优点缺点与取舍优化策略 五、其他进阶优化点六、总结 一、引言 架构思维:使用简洁的架构实现高性能读服务…...
永磁同步电机控制算法--基于PI的位置伺服控制
一、原理介绍 永磁同步伺服系统是包含了电流环、速度环和位置环的三环控制系统。 伺服系统通过电流检测电路和光电编码器检测电动机三相绕组电流和转子位置θ,通过坐标变换,计算出转矩电流分量iq和励磁电流分量id。 位置信号指令与实际转子位置信号的差…...
P1603 斯诺登密码详解
这个题目,我详细讲题解的两种方法,洛谷里面的题解,我是觉得大部分的时候是差了点意思的,不是看不懂,就是新知识没人详细讲解,我也是经常破防 先看题目: 题目是什么意思: 1…...
计算方法实验六 数值积分
【实验性质】综合性实验。 【实验目的】理解插值型积分法;掌握复化积分法算法。 【实验内容】 1对 ,用复化梯形积分和变步长梯形积分求值(截断误差不超过)。 【理论基础】 积分在工程中有重要的应用,数值积分…...
avx指令实现FFT
avx指令实现FFT 参考代码实现的难点补充的avx指令fft_avx256实现可继续优化的点 C语言实现FFT变换参考的代码是参考大模型生成的代码,很明显其使用的是位反转和蝶形变换的方法实现的FFT变换。但是大模型无法正确的生成用avx指令写的FFT变换的算法,所以这…...
Nginx 核心功能之正反代理
目录 一、Nginx 二、正向代理 三、反向代理 四、Nginx 缓存 1. 缓存功能的核心原理和缓存类型 2. 代理缓存功能设置 五、Nginx rewrite和正则 (1)Nginx 正则 (2)nginx location (3)Rewrite &…...
function包装器的意义
一:function包装器的概念 function包装器 也叫作适配器。C中的function本质是一个类模板,也是一个包装器。 二:需要function包装器的场景 那么我们来看看,我们为什么需要function呢? 一个需要包装器的场景:…...
【ThinkBook 16+ 电脑重做系统type-c接口部分功能失效解决方案】
ThinkBook 16 电脑重做系统type-c接口部分功能失效解决方案 问题回顾:重做电脑后,type-c接口部分功能失效,充电正常,连接外置硬盘正常,无法连接外拓显示器,显示usbc无信号(不同设备可能显示不同…...
【言语理解】中心理解题目之选项分析
front:中心理解题目之结构分析 4.1两出处六有误 两出处 背景、例子、分析论证中提炼的选项出处有误,一般不选但是和因此之前、不是而是 的不是部分、被指代部分提炼的选项出处有误,一般不选。 六有误 片面:原文并列谈论两方面,只…...
[原创](现代Delphi 12指南):[macOS 64bit App开发]: [1]如何加载动态链接库, 并无缝支持原生底层开发?
[作者] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 开发工具: Visual Studio、Delphi、XCode、…...
VTK入门指南
什么是VTK VTK (Visualization Toolkit) 是一个开源的、跨平台的计算机图形学、图像处理和可视化系统。它提供了丰富的算法和高级工具,用于3D计算机图形学、图像处理和可视化。 安装VTK Windows平台 下载预编译版本: 从VTK官网或GitHub发布页面下载 …...
开始一个vue项目-day2
这次新增的功能有: 1、使用cookie存储token 参考网站:https://vueuse.org/ 安装包: npm i vueuse/integrations npm i universal-cookie^7 2、cookie的设置读取和删除,代码:composables/auth.js import { useCookies } from …...
Baklib驱动企业知识管理AI升级
Baklib如何实现知识AI化 Baklib通过构建企业级知识中台的核心能力,将人工智能技术深度融入知识管理的全生命周期。其底层架构采用自然语言处理(NLP)与机器学习算法,实现对企业文档的智能分类与语义解析。例如,系统可自…...
Linux线程同步机制深度解析:信号量、互斥锁、条件变量与读写锁
Linux线程同步机制深度解析:信号量、互斥锁、条件变量与读写锁 一、线程同步基础 在多线程编程中,多个线程共享进程资源(如全局变量、文件描述符)时,若对共享资源的访问不加控制,会导致数据不一致或竞态条…...
js逆向绕过指纹识别
一、兼容性说明 官方支持 curl_cffi 明确支持 Windows 平台,并提供了预编译的安装包。其核心功能(如浏览器指纹模拟、HTTP/2 支持)在 Windows 上与 Linux/macOS 表现一致。 版本要求 • Python 3.8 及以上版本(推荐 Pyth…...
笔记整理六----OSPF协议
OSPF 动态路由的分类: 1.基于网络范围进行划分--将网络本身划分为一个个AS(自治系统---方便管理和维护) 内部网关协议---负责AS内部用户之间互相访问使用的协议 IGP--RIP EIGRP ISIS OSPF 外部网关协议--负责AS之间(整个互联网&…...
USB Type-C是不是全方位优于其他USB接口?
首先,USB TypeC接口内部引脚呈中心对称分布,正插、反插都能用,所以可以肓插,使用起来非常方便顺手。 其次,USB TypeC接口体积很小,特别是很薄,几乎适用于所有设备。而USB TypeA就是因为不方便应…...
信息系统监理师第二版教材模拟题第一组(含解析)
信息系统监理基础 信息系统监理的核心目标是( ) A. 降低项目成本 B. 确保项目按合同要求完成 C. 提高开发人员技术水平 D. 缩短项目周期答案:B 解析:信息系统监理的核心目标是确保信息系统工程项目按照合同要求、技术标准和规范完成,保障项目质量、进度和投资控制。 下列哪…...
NPP库中libnppist模块介绍
1. libnppist 模块简介 libnppist 是 NPP 库中专注于 图像统计分析与直方图计算 的模块,提供 GPU 加速的统计操作,适用于计算机视觉和图像处理中的特征提取与分析。 核心功能包括: 直方图计算(支持单通道/多通道) 统…...
k230摄像头初始化配置函数解析
通过 csi id 和图像传感器类型构建 Sensor 对象。 在图像处理应用中,用户通常需要首先创建一个 Sensor 对象。CanMV K230 软件可以自动检测内置的图像传感器,无需用户手动指定具体型号,只需设置传感器的最大输出分辨率和帧率。有关支持的图像…...
Spring的循环依赖问题
文章目录 一、什么是循环依赖?二、Spring 是如何解决循环依赖的?1.三级缓存2.解决循环依赖的流程 三、三级缓存机制可以解决所有的循环依赖问题吗?1. 为什么三级缓存在这里无效?2. 如何解决构造器循环依赖? 四、循环依…...
华为鸿蒙PC:开启国产操作系统自主化新纪元
——全栈自研、生态重构与未来挑战 2025年5月,一个值得中国科技界铭记的时间点。华为正式推出首款搭载鸿蒙操作系统(HarmonyOS)的PC产品。乍一听这像是又一款新电脑的发布,但它背后的意义远比表面更深远——这是中国首次推出从操…...
【LeetCode Hot100】动态规划篇
前言 本文用于整理LeetCode Hot100中题目解答,因题目比较简单且更多是为了面试快速写出正确思路,只做简单题意解读和一句话题解方便记忆。但代码会全部给出,方便大家整理代码思路。 70. 爬楼梯 一句话题意 每次爬1or2,问爬到n的路…...
【Java JUnit单元测试框架-60】深入理解JUnit:Java单元测试的艺术与实践
在当今快节奏的软件开发环境中,保证代码质量的同时又要快速交付成为了开发者面临的主要挑战。单元测试作为软件测试金字塔的基石,为我们提供了一种高效的解决方案。而在Java生态系统中,JUnit无疑是单元测试框架的代名词。本文将全面探讨JUnit…...
Java运算符学习笔记
运算符 -运算符介绍 运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。 算数运算符赋值运算符关系运算符[比较运算符]逻辑运算符位运算符[需要二进制基础]三元运算符 -算数运算符 介绍 算数运算符是对数值类型的变量进行运算的,在Java程…...
shell编程补充内容(Linux课程实验3)
一、求前五个偶数的和 1.这里先介绍要用到的expr 1. 整数计算 # 加法(注意运算符两侧空格) $ expr 10 20 30# 带括号的运算(需要转义) $ expr \( 10 20 \) \* 2 60# 取模运算 $ expr 15 % 4 注意:仅支持整数&…...
iview table组件 自定义表头
在实际项目开发中,我们经常会用到各种各样的表格,比如在表格表头中填加按钮,下拉菜单,图标等等,在网上搜了一段时间发现比较少,所以写好之后就想着分享出来给有需要的人参考参考,例如下面这种表…...
二叉搜索树实现删除功能 Java
在开始编写删除功能之前,先要编写好searchParent()(寻找父节点)和min()(查找树中最小值)两个函数,后期会在删除功能中使用到。 searchParent()的编写 /*** * param value* return Node*/public Node searchParent(int value){if(rootnull) return null;…...
Android Framework学习三:zygote剖析
文章目录 Zygote工作内容起始点初始化步骤启动 ZygoteInitZygoteInit.main () 函数内部操作 Zygote如何启动SystemServer参与的类和文件流程步骤进程创建完成后的处理 Framework学习之系列文章 在 Android 系统中,Zygote 是一个非常关键的进程,有 “App …...
LLM-Based Agent及其框架学习的学习(三)
文章目录 摘要Abstract1. 引言2. 推理与规划2.1 推理2.2 规划2.2.1 计划指定2.2.2 计划反思 3. 迁移与泛化3.1 未知任务的泛化3.2 情景学习3.3 持续学习 4. 学习Crewai和LangGraph4.1 Crewai4.2 LangGraph 参考总结 摘要 本文系统阐述了基于大语言模型的智能体在认知架构中的核…...
修复笔记:获取 torch._dynamo 的详细日志信息
一、问题描述 在运行项目时,遇到与 torch._dynamo 相关的报错,并且希望获取更详细的日志信息以便于进一步诊断问题。 二、相关环境变量设置 通过设置环境变量,可以获得更详细的日志信息: set TORCH_LOGSdynamo set TORCHDYNAM…...
阿里云服务器全栈技术指导手册(2025版)
阿里云服务器全栈技术指导手册(2025版) 一、基础配置与核心架构设计 1. 精准实例选型策略 • 通用计算场景:选择ECS通用型(如ecs.g7)实例,搭载第三代Intel Xeon处理器,适合Web应用、中小型数…...
llfc项目笔记客户端TCP
一、整体架构流程图(简洁版) 复制代码 【客户端启动】 |--- 初始化TcpMgr(单例)|--- 连接信号初始化:连接成功、断开、错误、发数据| 【用户操作:登录成功】|--- 触发发起跳转:发起连接(sig_connect_tcp)| 【TcpMgr收到连接请求】|--- 连接到服务器(connectToHost)…...
基于python的task--时间片轮询
目录 前言 utf-8 chinese GB2312 utf-8 排除task.c chinese GB2312 排除task.c 运行结果 前言 建议是把能正常工作的单个功能函数放到一起(就和放while函数里的程序一样),程序会按顺序自动配置。 不同的格式已经对应给出。 utf-8 impo…...
《前端秘籍:SCSS阴影效果全兼容指南》
在前端开发的旅程中,为网页元素添上阴影效果,就像为一幅画作点缀光影,能让页面瞬间生动起来,赋予元素层次感与立体感。可当我们满心欢喜地在SCSS中写下阴影代码,满心期待着在各种浏览器中都呈现出完美效果时࿰…...
强化学习机器人模拟器——RobotApp:一个交互式强化学习模拟器
RobotApp 是一个基于 Python 和 Tkinter 的交互式强化学习(Reinforcement Learning, RL)模拟器,集成了 GridWorld 环境和 QAgent 智能体,支持 Q-learning、SARSA 和 SARSA(λ) 算法。本博客将详细解析 robot_app.py 的功能、架构和使用方法,展示其如何通过直观的 GUI 界面…...
2025-04-26-利用奇异值重构矩阵-美团
2025-04-26-利用奇异值重构矩阵-美团 题目内容 在一家致力于图像处理的科技公司,你被分配到一个新项目,目标是开发一种图像压缩算法,以减少存储空间并加速传输。团队决定使用奇异值分解( S V D SVD SVD)对图像进行降…...
《解锁SCSS算术运算:构建灵动样式的奥秘》
SCSS作为CSS预处理器,算术运算功能犹如一颗璀璨明珠,赋予我们动态计算样式属性值的强大能力,让网页样式不再是一成不变的刻板呈现,而是能够根据各种条件和需求灵动变化。 在SCSS的世界里,算术运算绝非孤立的存在&…...