从哈希桶角度看 unordered_map 与 unordered_set 的实现

文章目录

  • 一、引言
  • 二、C++ unordered系列的无序关联式容器概览
  • 三、基于哈希桶的C++ unordered系列数据结构模拟实现
    • 1、unordered_map的模拟实现
    • 2、unordered_set的模拟实现
    • 3、哈希桶及其迭代器实现的代码
  • 四、扩展与应用
    • 1. 自定义哈希函数
    • 2. 其他unordered数据结构
      • unordered_multimap与unordered_map
      • unordered_multiset与unordered_set
    • 3. 实际应用案例分析

一、引言

哈希函数与哈希桶是计算机科学中用于实现高效数据检索的重要工具。在之前的博客中,我们已经详细探讨了哈希的基本概念、哈希函数的构造方法以及哈希桶的工作原理等内容。在本篇博客中,我们将进一步深入探索C++中的unordered系列数据结构,并利用之前介绍的哈希桶原理进行模拟实现。

C++11提供的unordered系列数据结构,如unordered_mapunordered_set等,是STL(Standard Template Library)中提供的一组非常重要的容器。它们以哈希表为基础,通过哈希函数将键映射到存储位置,从而实现了快速的插入、删除和查找操作。这些数据结构在处理大规模数据时,能够展现出比有序容器(如map、set)更高的性能。在C++11中,提供了的4个unordered系列的关联式容器。这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同。

本文将使用哈希桶对C++ unordered系列数据结构进行模拟实现。文本前置内容,请点击此处: 哈希技术解析:从哈希函数到哈希桶迭代器的全面指南


二、C++ unordered系列的无序关联式容器概览

1. unordered_map与unordered_multimap简介

数据结构特性:

  • unordered_map:C++ STL中的一个无序关联容器,它存储的元素是键值对,并且每个键在容器中唯一。其内部实现通常基于哈希表,通过哈希函数将键映射到存储位置,从而提供了常数时间复杂度的插入、删除和查找操作。
  • unordered_multimap:与unordered_map类似,但它允许键在容器中出现多次,即可以存储多个具有相同键的键值对。

应用场景:

  • unordered_map:当需要快速根据键查找对应的值时,或者当键的唯一性很重要时,unordered_map是一个很好的选择。例如,在缓存系统、词频统计等场景中,unordered_map可以高效地存储和检索键值对。
  • unordered_multimap:当需要存储多个具有相同键的键值对时,可以使用unordered_multimap。这在某些特定的应用场景中很有用,比如记录一个单词在文本中出现的所有位置。

2. unordered_set与unordered_multiset简介

数据结构特性:

  • unordered_set:是一个无序集合,它存储的元素是唯一的,不包含重复的元素。其内部实现也基于哈希表,通过哈希函数将元素映射到存储位置,从而实现了常数时间复杂度的插入、删除和查找操作。
  • unordered_multiset:与unordered_set类似,但它允许集合中包含重复的元素。

应用场景:

  • unordered_set:当需要快速检查一个元素是否存在于集合中,或者需要维护一个不包含重复元素的集合时,unordered_set是一个合适的选择。例如,在算法中去除重复元素、实现并集和交集运算等场景,unordered_set都能提供高效的解决方案。
  • unordered_multiset:当需要统计元素的出现次数或者需要维护一个包含重复元素的集合时,可以使用unordered_multiset。这在某些特定的数据处理和分析任务中可能会很有用。

总的来说,C++的unordered系列数据结构提供了高效的无序存储和检索机制,适用于各种需要快速处理大量数据的场景。通过合理地选择和使用这些数据结构,可以显著提高程序的性能和效率。

非unordered系列数据结构点击此处:深入解析C++树形关联式容器:map、set及其衍生容器的使用与原理-CSDN博客


三、基于哈希桶的C++ unordered系列数据结构模拟实现

1、unordered_map的模拟实现

  • 使用自定义哈希桶存储键值对

    unordered_map的简化模板类定义(注:hash_bucket是我实现的哈希桶所在的命名空间) :

    template<class K, class V, class Hash = HashFunc<K>>
    class unordered_map
    {struct MapKeyOfT {const K& operator()(const pair<K, V>& kv) { return kv.first; }};
    public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;// .... 
    private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
    };
    

    模板参数K:键(Key)的类型。V:值(Value)的类型。Hash:哈希函数(Hash Function)的类型,默认为HashFunc<K>,即默认以K来计算哈希位置。该参数也在哈希桶部分有介绍。

    内部结构体 MapKeyOfT:这个结构体定义了一个调用运算符,用于从pair<K, V>中提取键。这是哈希表内部可能需要的,以便能够根据键来定位存储的元素。

    迭代器类型定义typedef语句定义了一个迭代器类型,表示指向hash_bucket::HashTable中元素的迭代器。这个迭代器类型用于公开unordered_map的接口,以便用户可以遍历集合中的元素。

    私有成员_htunordered_map的私有成员,其类型为hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> 。这个哈希表用于存储键值对,并根据键的哈希值来定位元素。

  • 实现插入、查找、删除等基本操作

    operator[]用于访问或插入一个键值对。如果键k已经存在于unordered_map中,则该函数返回该键对应的值的引用;如果键k不存在,则该函数插入一个新的键值对(键为k,值为V()的默认构造实例),并返回新插入值的引用:

    V& operator[](const K& k) {pair<iterator, bool> it = insert({ k,V() });return (*it.first).second;//return (*((this->insert(make_pair(k, V()))).first)).second;
    }
    

    在这段代码中,insert成员函数被调用,它尝试插入一个键值对到unordered_map中。insert返回一个pair<iterator, bool>,其中迭代器指向新插入的元素(或已存在的元素),布尔值表示是否实际插入了新元素。由于unordered_map不允许重复的键,所以对于operator[]来说,这个布尔值总是true,除非在插入过程中发生了异常。然后,通过解引用迭代器it.first来获取键值对的引用,并返回其second成员(即值)的引用。

    pair<iterator,bool> insert(const pair<K, V>& kv) { return _ht.Insert(kv); }  
    bool erase(const K& k) { return _ht.Erase(k); }  
    iterator find(const K& k) { return _ht.Find(k); }
    
    • insert函数接收一个pair<K, V>类型的参数,并调用_ht.Insert方法尝试将其插入哈希表中。它返回一个pair<iterator, bool>,其中迭代器指向新插入的元素(或已存在的元素),布尔值表示是否成功插入了新元素。
    • erase函数接收一个键类型的参数,并调用_ht.Erase方法尝试从哈希表中删除具有该键的键值对。它返回一个布尔值,表示删除操作是否成功。
    • find函数接收一个键类型的参数,并调用_ht.Find方法查找具有该键的键值对。如果找到,它返回一个指向该键值对的迭代器;否则,返回end()迭代器。
  • 封装哈希桶迭代器

    我们的哈希桶已实现了绝大部分的功能,因此我们此处仅仅调用其函数即可。

    typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;	
    iterator begin() { return _ht.begin(); }
    iterator end() { return _ht.end(); }
    

2、unordered_set的模拟实现

  • 利用哈希桶存储唯一元素

    unordered_set的简化模板类定义(注:hash_bucket是我实现的哈希桶所在的命名空间) :

    template<class K, class Hash = HashFunc<K>>
    class unordered_set{struct SetKeyOfT{const K& operator()(const K& key) { return key; }};
    public:typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::iterator iterator;//...
    private:hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
    };
    

    模板参数unordered_set模板接受两个类型参数,KHashK是集合中元素的键类型,Hash是哈希函数类型,用于计算键的哈希值。默认情况下,如果没有提供Hash,它将使用HashFunc<K>作为哈希函数。

    内部结构体SetKeyOfT:这是一个简单的函数对象(或称为仿函数),它重载了operator()以返回其输入的引用。在unordered_set的上下文中,它用于从键中提取键本身(在这种情况下,键就是元素本身)。这是为了与hash_bucket::HashTable的接口保持一致,该接口可能期望一个可以从某种类型中提取键的函数对象。

    迭代器类型定义:使用typedef语句定义了一个名为iterator的类型别名,它表示指向hash_bucket::HashTable中元素的迭代器。这个迭代器类型用于公开unordered_set的接口,以便用户可以遍历集合中的元素。

    私有成员变量_htunordered_set的一个私有成员变量,其类型为hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>。这表示它是一个哈希表,用于存储unordered_set中的元素。键类型是const K,因为集合中的元素不应被修改(在unordered_set中,元素是唯一的,并且一旦插入就不能被修改)。SetKeyOfT用于从键中提取键,而Hash是用于计算哈希值的函数。

  • 实现集合的基本操作

    pair<iterator,bool> insert(const K& k) { return _ht.Insert(k); }
    bool erase(const K& k) { return _ht.Erase(k); }
    iterator find(const K& k) { return _ht.Find(k); }
    
    • insert函数尝试在集合中插入一个元素,并返回一个pair<iterator, bool>,其中迭代器指向新插入的元素(或已存在的元素),布尔值表示是否实际插入了新元素。由于unordered_set不允许重复元素,所以如果尝试插入一个已经存在的元素,该函数不会插入新元素,而是返回指向已存在元素的迭代器,并将布尔值设置为false
  • 封装哈希桶迭代器

    此处与unordered_map相同。

    typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::iterator iterator;
    iterator begin() { return _ht.begin(); }
    iterator end() { return _ht.end(); }
    

3、哈希桶及其迭代器实现的代码

该文已详细叙述哈希桶相关内容 ->深入解析C++树形关联式容器:map、set及其衍生容器的使用与原理-CSDN博客

template<class K>
struct HashFunc {size_t operator()(const K& key) { return (size_t)key; }
};
template<>
struct HashFunc<string> {size_t operator()(const string& s) {size_t hashi = 0;for (auto& e : s) {hashi += e;hashi *= 31;}return hashi;}
};
namespace hash_bucket
{template<class T>struct HashNode {HashNode<T>* _next;T _data;HashNode(const T& data) :_next(nullptr), _data(data) {}};template<class K, class T, class KeyOfT, class Hash >class HashTable;template<class K, class T, class KeyOfT, class Hash>struct  __HTIterator {typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HTIterator<K, T, KeyOfT, Hash> Self;Node* _node;HT* _ht;__HTIterator(Node* node, HT* ht) :_node(node), _ht(ht) {}T& operator*() { return _node->_data; }T* operator->() { return &_node->_data; }bool operator!=(const Self& s)const { return _node != s._node; }bool operator==(const Self& s) const { return _node == s._node; }Self& operator++() {if (_node->_next) {_node = _node->_next;}else {KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();hashi++;while (hashi < _ht->_tables.size()) {if (_ht->_tables[hashi]) {_node = _ht->_tables[hashi];break;}hashi++;}if (hashi == _ht->_tables.size()) {_node = nullptr;}}return *this;}};template<class K, class T, class KeyOfT, class Hash >class HashTable {typedef HashNode<T> Node;template<class K, class T, class KeyOfT, class Hash>friend struct __HTIterator;public:typedef __HTIterator<K, T, KeyOfT, Hash> iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); i++)if (_tables[i])return iterator(_tables[i], this);return end();}iterator end() { return iterator(nullptr, this); }HashTable()//:kot(KeyOfT()),hs(Hash()){_tables.resize(10, nullptr);_n = 0;kot = KeyOfT();hs = Hash();} ~HashTable() {for (size_t i = 0; i < _tables.size(); i++) {Node* cur = _tables[i];while (cur) {Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}pair<iterator,bool> Insert(const T& data) {iterator it = Find(kot(data));if (it != end())return { it,false};if (_n == _tables.size()) {vector<Node*> newTables(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur) {Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % newTables.size();cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hs(kot(data)) % _tables.size();Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return { iterator(newnode, this),true };}bool Erase(const K& key) {size_t hashi = hs(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key) {// 删除if (prev)prev->_next = cur->_next;else_tables[hashi] = cur->_next;delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}iterator Find(const K& key) {size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key)return iterator(cur, this);cur = cur->_next;}return iterator(nullptr, this);}private:vector<Node*> _tables;size_t _n;KeyOfT kot; Hash hs;};
}

四、扩展与应用

1. 自定义哈希函数

在C++中,当使用std::unordered_setstd::unordered_map等无序容器时,哈希函数起着至关重要的作用。默认的哈希函数对于许多类型都工作得很好,但有时对于自定义类型或特殊需求,默认的哈希函数可能不是最优的,甚至可能导致性能下降或哈希冲突过多。

为特定类型设计哈希函数

对于自定义类型,需要提供一个哈希函数,该函数接受自定义类型的对象作为参数,并返回一个足够大的整数值。设计哈希函数时,需要确保:

  • 不同的对象尽可能映射到不同的哈希值。
  • 相同的对象总是映射到相同的哈希值。
  • 哈希函数的计算应该尽可能快。

例如,对于一个包含字符串和整数的自定义类型,可以使用字符串的哈希值和整数的哈希值的组合作为整体的哈希值。

2. 其他unordered数据结构

除了unordered_setunordered_map之外,标准库还提供了unordered_multimapunordered_multiset。这两个数据结构分别允许存储具有相同键的多个值对和多个值。

unordered_multimap与unordered_map

unordered_multixxxunordered_xxx的主要区别在于前者允许键重复,而后者不允许。具体来说:

  • 键的重复性unordered_map中的每个键都是唯一的,每个键只能映射到一个值。而unordered_multimap允许键的重复,这意味着同一个键可以映射到多个值。
  • 使用场景:当你需要存储键值对,并且每个键只对应一个值时,unordered_map是合适的选择。而如果你需要存储的键值对中有多个键是相同的,并且每个键对应多个值,那么unordered_multimap更为合适。
  • 内部实现:两者都使用哈希表作为底层数据结构,以实现快速的插入、删除和查找操作。但由于unordered_multimap允许键重复,因此在处理冲突和存储键值对时可能需要更复杂的逻辑。

unordered_multiset与unordered_set

  • 元素的重复性unordered_set中的每个元素都是唯一的,不允许有重复元素。而unordered_multiset则允许元素重复,即集合中可以包含多个相同的元素。
  • 使用场景:当你需要存储一个不包含重复元素的集合时,unordered_set是合适的选择。而如果你需要存储的集合中可能包含重复的元素,那么unordered_multiset更为合适。
  • 内部实现:两者都使用哈希表作为底层数据结构,以实现快速的插入、删除和查找操作。但由于unordered_multiset允许元素重复,因此在处理冲突和存储元素时可能需要更复杂的逻辑。

在实际应用中,根据具体的需求和数据特性选择合适的数据结构是非常重要的。例如,在需要统计词频的场景中,由于一个单词可能在文本中出现多次,因此使用unordered_multiset来存储单词和它们的出现次数会更合适。而在某些需要唯一标识的场景中,如用户ID的存储,使用unordered_set来确保ID的唯一性则更为合适。

3. 实际应用案例分析

unordered系列数据结构在实际项目中有着广泛的应用,特别是在需要快速查找和插入的场景中。

案例一:词频统计

在处理大量文本数据时,词频统计是一个常见的任务。可以使用unordered_map来存储每个单词及其出现的次数。由于哈希表提供了平均常数时间的查找和插入操作,因此这种方法在处理大规模文本时非常高效。

案例二:缓存系统

在缓存系统中,通常需要快速查找和插入键值对。unordered_mapunordered_set可以用作缓存的底层数据结构,提供快速的访问速度。当缓存达到最大容量时,还可以使用这些数据结构来高效地执行替换策略(如LRU缓存替换)。

本文完整代码: unordered_map与unordered_set · 比奇堡的Zyb/每日学习 - 码云 - 开源中国 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/755984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯/慈善晚会/c\c++

问题描述 热心公益的G哥哥又来举办慈善晚会了&#xff0c;这次他邀请到了巴菲特、马云等巨富&#xff0c;还邀请到了大V、小C等算法界泰斗。晚会一共邀请了n位尊贵的客人&#xff0c;每位客人都位于不同的城市&#xff0c;也就是说每座城市都有且仅有一位客人。这些城市的编号为…

大模型知识库

一种利用 langchain 思想实现的基于本地知识库的问答应用&#xff0c;目标期望建立一套对中文场景与开源模型支持友好、可离线运行的知识库问答解决方案。 1. 下载Langchain-chatchat git clone https://github.com/chatchat-space/Langchain-Chatchat/ 2. 下载大模型和embe…

c++ 自己实现一个迭代器

具体代码 /*自定义迭代器的实现 */ #include <iostream> using namespace std; class num {int val; //具体的数字int length; //数字的位数void calculate_length(){if(val/100){ //这个数字只有1位length1;return;}int x10; //这里就是不断重复除直…

python连接mysql数据库步骤

连接MySQL数据库通常需要使用第三方库&#xff0c;而在Python中&#xff0c;mysql-connector-python 是一个常用的 MySQL 连接库。以下是连接 MySQL 数据库的步骤&#xff1a; 安装 MySQL 连接库&#xff1a; 使用以下命令安装 mysql-connector-python&#xff1a; pip insta…

汽车电子零部件(8):T_Box

前言: 网联汽车(Connected Vehicles ,CV)是一个广泛的概念,四个主要的CV线程已发展起来:互联、自主、共享和电动。这些应用于包括CV在内的垂直领域:汽车、通信、互联网和共享手机服务。中国汽车工程师学会(SAEC)提倡将车载ADAS(高级驾驶员辅助系统)与通信技术相结合…

gitlab仓库使用流程(开发)

1.1.GitLab代码提交流程&#xff1a; 1.1.1准备阶段&#xff1a; 确保已经安装了Git&#xff0c;并且配置了正确的用户名和邮箱地址。 在本地创建一个新的文件夹&#xff0c;用于存放即将开发的代码。 1.1.2.拉取代码&#xff1a; 使用git clone命令从GitLab上拉取项目代码…

基于python的在线学习与推荐系统

技术&#xff1a;pythonmysqlvue 一、系统背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的大…

java数据库关系数据库设计(下)

目录 1.概念模型:E-R图 1.实体 2.属性 3.联系 4.实体间的映射关系 2.物理模型:数据库模型图 3.数据库规范设计 1.常见数据库设计问题 2.三大范式 1.第一范式 2.第二范式 3.第三范式 3.规范化和性能的关系 1.概念模型:E-R图 在数据库需求分析阶段&#xff…

阿里5年经验之谈 —— 接口测试用例如何编写?

接口测试用例如何编写&#xff1f;下面简单给大家讲解一下。 接口测试用例是目前软件开发中不可或缺的一个重要部分&#xff0c;因此编写接口测试用例同样重要。 接口测试用例的作用非常明显&#xff0c;它能够帮助我们了解产品正在考验、调整它如何表现在特定情境之下、产品…

每日一题:C语言经典例题之求n!

题目描述 输入一个正整数n&#xff08;1<n<12)&#xff0c;求n!。 输入 输入一个正整数n。 输出 输出n!。 样例输入&#xff1a; 3 样例输出&#xff1a; 6 代码&#xff1a; #include <stdio.h> int f(int n) {if(n1)return 1;else{return n*f(n-1);} } int main(…

01|模型IO:输入提示、调用模型、解析输出

Model I/O 可以把对模型的使用过程拆解成三块&#xff0c;分别是输入提示&#xff08;对应图中的Format&#xff09;、调用模型&#xff08;对应图中的Predict&#xff09;和输出解析&#xff08;对应图中的Parse&#xff09;。这三块形成了一个整体&#xff0c;因此在LangCha…

VScode 设置个性化背景(保姆级教程)

VS Code设置个性化背景的作用主要体现在以下几个方面&#xff1a; 提升编程体验&#xff1a;个性化背景能够让编程环境更符合个人的审美和习惯&#xff0c;使得长时间在VS Code中进行代码编辑时&#xff0c;能够保持愉悦的心情&#xff0c;从而提高编程效率。减少视觉疲劳&…

【工具】Mermaid + 大模型画流程图

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 引入使用画TCP三次握手了解历史人物 总结 引入 最近看面试文章关于TCP三次握手和…

数据预处理:重复值

数据重复值处理 数据重复值出现情况重复的记录用于分析演变规律重复的记录用于样本不均衡处理重复的记录用于检测业务规则问题 数据重复值出现情况 数据集中的重复值包括以下两种情况&#xff1a; 数据值完全相同的多条数据记录。这是最常见的数据重复情况。数据主体相同但匹…

云贝教育 |【技术文章】POSTGRESQL FDW应用

注: 本文为云贝教育 刘峰 原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎和未经注明出处的转载。 前言 Wrapper&#xff08;FDW&#xff09;是一项关键特性&#xff0c;它赋予数据库用户直接通过SQL语句访问存储于外部数据源的能…

【AcWing】蓝桥杯集训每日一题Day6|多路归并|贪心|1262.鱼塘钓鱼(C++)

1262.鱼塘钓鱼 1262. 鱼塘钓鱼 - AcWing题库难度&#xff1a;简单时/空限制&#xff1a;1s / 64MB总通过数&#xff1a;3449总尝试数&#xff1a;5251来源&#xff1a;《信息学奥赛一本通》算法标签枚举贪心堆多路归并 题目内容 有N个鱼塘排成一排&#xff0c;每个鱼塘中有一…

XAI之TDB:transformer-debugger的简介、安装和使用方法、应用案例之详细攻略

XAI之TDB&#xff1a;transformer-debugger的简介、安装和使用方法、应用案例之详细攻略 导读&#xff1a;小语言模型在处理一些任务时会出现无法明确解释的行为&#xff0c;难以细致追踪模型内部各个组件如神经元、注意力头等在推理过程中的作用。2024年3月12日&#xff0c;Op…

第二十六天-统计与机器学习SciPy,Scikit-Leaen

目录 1.介绍 2.使用scipy 1. 安装 2.拟合曲线 3.随机变量与概率分布 4.假设检验 5.参数检验 3.使用Scikit-Learn 1. 机器学习库&#xff0c;建立在numpy,scipy,matplotlib基础上 2.包含功能 3.安装 1.官网&#xff1a;https://scikit-learn.org 2.下载 3.线性回归…

一文掌握python函数式编程及应用实例(超详细及超多应用实例)(二)

一.命名空间和作用域: Python 的命名空间和作用域是两个密切相关的概念,它们共同决定了变量的可见性、生命周期以及如何在程序中查找变量。 a.命名空间(Namespace) 命名空间 是一个存储变量名称及其对应值的地方。在 Python 中,每个模块、函数或类都有其自己的命名空间…

STM32 ADC库函数

单片机学习&#xff01; 目录 1. RCC_ADCCLKConfig 函数 2. ADC_DeInit 函数 3. ADC_Init 函数 4. ADC_StructInit 函数 5. ADC_Cmd 函数 6. ADC_DMACmd 函数 7. ADC_ITConfig 函数 8. 用于校准的函数 8.1 ADC_ResetCalibration 函数 8.2 ADC_GetResetCalibrationSta…