当前位置: 首页 > news >正文

双向链表在系统调度、游戏、文本编辑及组态方面的应用

在编程的奇妙世界里,数据结构就像是一把把神奇的钥匙(前面我们介绍过单向链表的基础了,这里我们更进一步),能帮我们打开解决各种问题的大门。今天,咱们就来聊聊其中一把特别的钥匙——双向链表。双向链表和普通链表相比,每个节点不仅有指向下一个节点的指针,还有指向前一个节点的指针,这就好比给它装上了“前后眼”,在很多场景下都能大显身手。接下来,我们通过双向链表在系统管理、游戏、文本编辑及组态软件中的应用来展示其强大,并给出示例代码。

文章目录

    • 1. 操作系统中的进程调度
      • 1.1 进程调度的需求
      • 1.2 双向链表的优势
      • 1.3 示例代码
        • 1.3.1 代码解释
    • 2. 双向链表在游戏开发中的应用
      • 2.1 游戏对象管理
        • 2.1.1 需求背景
        • 2.1.2 双向链表的优势
        • 2.1.3 示例代码
        • 2.1.4 代码解释
    • 3. 文本编辑器中的撤销与重做功能
      • 3.1 撤销与重做功能的原理
      • 3.2 双向链表的应用
      • 3.3 示例代码
        • 代码解释
    • 4. 缓存机制中的LRU缓存
      • 4.1 LRU缓存的原理
      • 4.2 双向链表与哈希表结合实现LRU缓存
      • 4.3 示例代码
        • 代码解释
    • 5. 双向链表在组态软件中的应用
      • 5.1 设备连接与监控
        • 5.1.1 需求背景
        • 5.1.2 双向链表的优势
        • 5.1.3 示例代码
        • 5.1.4 代码解释
          • `Device` 结构体
          • `DeviceList` 类
          • `addDevice(int id, const std::string& type, const std::string& address)` 方法
          • `removeDevice(int id)` 方法

1. 操作系统中的进程调度

1.1 进程调度的需求

同学们可以把操作系统想象成一个超大型的任务管理中心,这里有大量的进程需要被管理和调度。为了让整个系统高效运行,需要一个数据结构来存储进程的各种信息,并且要能方便地进行插入新进程、删除已完成进程以及对进程进行排序和查找等操作。这就好比一个繁忙的机场,需要一个高效的航班管理系统来安排航班的起降和调度。

1.2 双向链表的优势

双向链表在进程调度中就发挥了很大的作用。每个进程可以看作是双向链表中的一个节点,节点里存储着进程的关键信息,像进程 ID、优先级、运行状态等。由于双向链表在插入和删除节点时的时间复杂度是 O ( 1 ) O(1) O(1),这使得操作系统在创建新进程、结束进程或者调整进程优先级时能够快速完成。而且双向链表支持双向遍历,操作系统可以根据不同的调度算法,灵活地从前往后或者从后往前遍历链表,选择合适的进程来执行。

1.3 示例代码

#include <iostream>// 定义进程结构体
struct Process {int pid;int priority;Process* prev;Process* next;Process(int id, int prio) : pid(id), priority(prio), prev(nullptr), next(nullptr) {}
};// 定义双向链表类
class ProcessList {
private:Process* head;
public:ProcessList() : head(nullptr) {}// 插入新进程(按照优先级插入)void insertProcess(int pid, int priority) {Process* newProcess = new Process(pid, priority);if (!head || priority < head->priority) {newProcess->next = head;if (head) head->prev = newProcess;head = newProcess;} else {Process* current = head;while (current->next && priority >= current->next->priority) {current = current->next;}newProcess->next = current->next;newProcess->prev = current;if (current->next) current->next->prev = newProcess;current->next = newProcess;}}// 删除进程void deleteProcess(int pid) {Process* current = head;while (current && current->pid != pid) {current = current->next;}if (current) {if (current->prev) {current->prev->next = current->next;} else {head = current->next;}if (current->next) {current->next->prev = current->prev;}delete current;}}// 遍历链表void printProcesses() {Process* current = head;while (current) {std::cout << "PID: " << current->pid << ", Priority: " << current->priority << std::endl;current = current->next;}}
};
1.3.1 代码解释
  • 整体构思设计:我们要模拟操作系统中的进程调度,使用双向链表来存储进程信息。通过自定义的 Process 结构体表示进程节点,ProcessList 类来管理这些节点,实现进程的插入、删除和遍历功能。
  • Process 结构体
    • 包含进程的基本信息 pid(进程ID)和 priority(优先级)。
    • prevnext 指针分别指向前一个和后一个进程节点,用于构建双向链表。
    • 构造函数 Process(int id, int prio) 用于初始化进程节点。
  • ProcessList
    • insertProcess 方法
      • 功能:按照进程的优先级将新进程插入到合适的位置。
      • 实现思路:首先创建一个新的进程节点 newProcess。如果链表为空或者新进程的优先级比头节点的优先级高,则将新节点作为头节点。否则,遍历链表找到合适的插入位置,使得链表中的进程按照优先级从小到大排列。
    • deleteProcess 方法
      • 功能:根据进程ID删除指定的进程节点。
      • 实现思路:遍历链表找到具有指定 pid 的进程节点。如果找到,调整该节点前后节点的指针,将其从链表中移除,然后释放该节点的内存。
    • printProcesses 方法
      • 功能:遍历链表并打印每个进程的信息。
      • 实现思路:从头节点开始,依次访问每个节点,打印其 pidpriority 信息,然后移动到下一个节点,直到链表结束。

2. 双向链表在游戏开发中的应用

2.1 游戏对象管理

2.1.1 需求背景

想象一下,我们正在开发一款超酷的游戏,里面有各种各样的游戏对象,像角色、道具、怪物等等。游戏运行过程中,这些对象会不断地被创建、销毁,而且还需要对它们进行各种操作,比如移动、攻击、碰撞检测等。这时候,就需要一个高效的数据结构来管理这些游戏对象。

2.1.2 双向链表的优势

双向链表就非常适合这个场景。每个游戏对象可以作为链表中的一个节点,节点之间通过前后指针相连。这样,当我们需要添加新的游戏对象时,只需要在链表中插入一个新节点;当某个对象被销毁时,也能很方便地从链表中删除对应的节点。而且,双向链表支持双向遍历,我们可以根据需要从前往后或者从后往前遍历链表,这在进行碰撞检测等操作时非常有用。

2.1.3 示例代码
#include <iostream>
#include <string>// 定义游戏对象结构体
struct GameObject {std::string name;int x;int y;GameObject* prev;GameObject* next;GameObject(const std::string& n, int _x, int _y) : name(n), x(_x), y(_y), prev(nullptr), next(nullptr) {}
};// 定义游戏对象链表类
class GameObjectList {
private:GameObject* head;
public:GameObjectList() : head(nullptr) {}// 添加游戏对象void addGameObject(const std::string& name, int x, int y) {GameObject* newObj = new GameObject(name, x, y);if (!head) {head = newObj;} else {newObj->next = head;head->prev = newObj;head = newObj;}}// 删除游戏对象void deleteGameObject(const std::string& name) {GameObject* current = head;while (current) {if (current->name == name) {if (current->prev) {current->prev->next = current->next;} else {head = current->next;}if (current->next) {current->next->prev = current->prev;}delete current;break;}current = current->next;}}// 遍历游戏对象void printGameObjects() {GameObject* current = head;while (current) {std::cout << "Name: " << current->name << ", Position: (" << current->x << ", " << current->y << ")" << std::endl;current = current->next;}}
};
2.1.4 代码解释
部分整体构思设计实现思路
GameObject 结构体为了表示游戏中的各种对象,我们定义了这个结构体。它包含了对象的名称和位置信息,以及用于构建双向链表的前后指针。成员变量 name 存储对象的名称,xy 存储对象的位置,prevnext 分别指向前一个和后一个节点。构造函数用于初始化这些成员。
GameObjectList这个类负责管理游戏对象链表,提供添加、删除和遍历游戏对象的功能。
addGameObject 方法首先创建一个新的游戏对象节点。如果链表为空,将新节点作为头节点;否则,将新节点插入到链表头部,更新前后指针。
deleteGameObject 方法遍历链表,找到名称匹配的游戏对象

3. 文本编辑器中的撤销与重做功能

3.1 撤销与重做功能的原理

在文本编辑器中,撤销与重做功能允许用户回退到之前的编辑状态或恢复之前撤销的操作。这需要一种数据结构来记录用户的每一步操作,并且能够方便地向前和向后追溯这些操作。

3.2 双向链表的应用

双向链表可以很好地实现这一功能。每一次用户的编辑操作(如插入字符、删除字符、修改文本等)都可以被记录为双向链表中的一个节点。节点中包含操作的详细信息,如操作类型、操作位置、操作内容等。当用户执行撤销操作时,程序可以沿着双向链表的前驱指针向前追溯到上一个操作状态,并还原相应的文本状态;当用户执行重做操作时,程序可以沿着双向链表的后继指针向后追溯到下一个操作状态,恢复之前撤销的操作。

3.3 示例代码

#include <iostream>
#include <string>// 定义操作结构体
struct EditOperation {std::string type;int position;std::string content;EditOperation* prev;EditOperation* next;EditOperation(const std::string& t, int pos, const std::string& c): type(t), position(pos), content(c), prev(nullptr), next(nullptr) {}
};// 定义操作链表类
class EditHistory {
private:EditOperation* head;EditOperation* current;
public:EditHistory() : head(nullptr), current(nullptr) {}// 添加新操作void addOperation(const std::string& type, int position, const std::string& content) {EditOperation* newOp = new EditOperation(type, position, content);if (!head) {head = newOp;current = newOp;} else {newOp->prev = current;current->next = newOp;current = newOp;}}// 撤销操作void undo() {if (current && current->prev) {current = current->prev;// 这里根据操作类型实现具体的文本还原逻辑std::cout << "Undo: " << current->type << " at position " << current->position << std::endl;}}// 重做操作void redo() {if (current && current->next) {current = current->next;// 这里根据操作类型实现具体的文本恢复逻辑std::cout << "Redo: " << current->type << " at position " << current->position << std::endl;}}
};
代码解释
  • 整体构思设计:为了实现文本编辑器的撤销与重做功能,使用双向链表来记录用户的编辑操作。通过 EditOperation 结构体表示每个编辑操作节点,EditHistory 类来管理这些节点,实现操作的添加、撤销和重做功能。
  • EditOperation 结构体
    • 包含编辑操作的信息,如 type(操作类型,如插入、删除等)、position(操作位置)和 content(操作内容)。
    • prevnext 指针用于构建双向链表。
    • 构造函数 EditOperation(const std::string& t, int pos, const std::string& c) 用于初始化操作节点。
  • EditHistory
    • addOperation 方法
      • 功能:添加新的编辑操作到链表中。
      • 实现思路:创建一个新的操作节点 newOp。如果链表为空,将其作为头节点,并将当前操作指针 current 指向它。否则,将新节点添加到链表尾部,并更新 current 指针。
    • undo 方法
      • 功能:撤销上一次操作。
      • 实现思路:如果当前操作指针 current 不为空且有前一个操作节点,将 current 指针移动到前一个节点,并输出撤销操作的信息。在实际应用中,还需要根据操作类型实现具体的文本还原逻辑。
    • redo 方法
      • 功能:重做被撤销的操作。
      • 实现思路:如果当前操作指针 current 不为空且有下一个操作节点,将 current 指针移动到下一个节点,并输出重做操作的信息。同样,在实际应用中需要根据操作类型实现具体的文本恢复逻辑。

4. 缓存机制中的LRU缓存

4.1 LRU缓存的原理

LRU(Least Recently Used)缓存是一种常用的缓存淘汰策略,它的原理是当缓存满时,优先淘汰最近最少使用的元素。这需要一种数据结构来记录元素的访问顺序,并且能够快速地找到最近最少使用的元素。

4.2 双向链表与哈希表结合实现LRU缓存

双向链表与哈希表结合可以高效地实现LRU缓存。双向链表用于维护元素的访问顺序,最近访问的元素放在链表头部,最近最少使用的元素放在链表尾部。哈希表用于快速定位双向链表中的节点,这样在访问元素时,我们可以通过哈希表快速找到该元素在双向链表中的位置,然后将其移动到链表头部,表示该元素被最近访问过。当缓存满时,直接删除链表尾部的节点即可。

4.3 示例代码

#include <iostream>
#include <unordered_map>// 定义缓存节点结构体
struct CacheNode {int key;int value;CacheNode* prev;CacheNode* next;CacheNode(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {}
};// 定义LRU缓存类
class LRUCache {
private:int capacity;std::unordered_map<int, CacheNode*> cache;CacheNode* head;CacheNode* tail;public:LRUCache(int cap) : capacity(cap), head(nullptr), tail(nullptr) {}// 获取元素int get(int key) {if (cache.find(key)!= cache.end()) {CacheNode* node = cache[key];moveToFront(node);return node->value;}return -1;}// 插入元素void put(int key, int value) {if (cache.find(key)!= cache.end()) {CacheNode* node = cache[key];node->value = value;moveToFront(node);} else {CacheNode* newNode = new CacheNode(key, value);cache[key] = newNode;if (!head) {head = newNode;tail = newNode;} else {addToFront(newNode);if (cache.size() > capacity) {removeTail();}}}}private:// 将节点移动到链表头部void moveToFront(CacheNode* node) {if (node == head) return;if (node == tail) {tail = node->prev;tail->next = nullptr;} else {node->prev->next = node->next;node->next->prev = node->prev;}addToFront(node);}// 将节点添加到链表头部void addToFront(CacheNode* node) {node->next = head;node->prev = nullptr;if (head) head->prev = node;head = node;if (!tail) tail = head;}// 删除链表尾部节点void removeTail() {if (!tail) return;CacheNode* toDelete = tail;if (tail == head) {head = nullptr;tail = nullptr;} else {tail = tail->prev;tail->next = nullptr;}cache.erase(toDelete->key);delete toDelete;}
};
代码解释
  • 整体构思设计:为了实现 LRU 缓存,我们结合使用双向链表和哈希表。双向链表用于维护元素的访问顺序,哈希表用于快速查找元素。通过 CacheNode 结构体表示缓存节点,LRUCache 类来管理缓存的插入、获取和淘汰操作。
  • CacheNode 结构体
    • 包含缓存元素的 keyvalue
    • prevnext 指针用于构建双向链表。
    • 构造函数 CacheNode(int k, int v) 用于初始化缓存节点。
  • LRUCache
    • get 方法
      • 功能:根据 key 获取缓存中的元素。
      • 实现思路:首先在哈希表中查找 key。如果找到,将对应的节点移动到链表头部(表示该元素最近被访问),然后返回节点的值。如果未找到,返回 -1。
    • put 方法
      • 功能:插入或更新缓存元素。
      • 实现思路:如果 key 已经存在于缓存中,更新节点的值并将其移动到链表头部。如果 key 不存在,创建一个新的节点,将其添加到链表头部,并插入到哈希表中。如果缓存已满,删除链表尾部的节点(最近最少使用的元素)。
    • moveToFront 方法
      • 功能:将指定节点移动到链表头部。
      • 实现思路:如果节点已经是头节点,直接返回。如果节点是尾节点,更新尾节点指针。否则,调整节点前后节点的指针,将其从原位置移除,然后调用 addToFront 方法将其添加到链表头部。
    • addToFront 方法
      • 功能:将节点添加到链表头部。
      • 实现思路:更新节点的 next 指针指向原头节点,原头节点的 prev 指针指向该节点,然后将头节点指针更新为该节点。如果链表原本为空,同时更新尾节点指针。
    • removeTail 方法

5. 双向链表在组态软件中的应用

5.1 设备连接与监控

5.1.1 需求背景

组态软件在工业自动化领域可是个得力助手,它要负责监控和控制大量的工业设备。想象一下,一个大型工厂里有各种各样的机器设备,像传感器、电机、阀门等等,这些设备都需要和组态软件建立连接,软件要实时获取它们的状态信息,比如设备是否正常运行、当前的温度、压力等参数。同时,还得能随时对设备进行控制,比如启动、停止、调节参数等。这就需要一个高效的数据结构来管理这些设备的连接信息。

5.1.2 双向链表的优势

双向链表非常适合用于管理设备连接信息。每个设备可以看作是双向链表中的一个节点,节点里存储着设备的关键信息,如设备 ID、设备类型、通信地址、连接状态等。当有新设备接入系统时,我们可以方便地在链表中插入一个新节点;当某个设备出现故障或者断开连接时,也能快速地从链表中删除对应的节点。而且,双向链表支持双向遍历,我们可以根据需要从前往后或者从后往前遍历链表,这在进行设备状态查询、故障排查等操作时非常有用。

5.1.3 示例代码
#include <iostream>
#include <string>// 定义设备结构体
struct Device {int deviceId;std::string deviceType;std::string communicationAddress;bool isConnected;Device* prev;Device* next;Device(int id, const std::string& type, const std::string& address): deviceId(id), deviceType(type), communicationAddress(address), isConnected(false), prev(nullptr), next(nullptr) {}
};// 定义设备链表类
class DeviceList {
private:Device* head;
public:DeviceList() : head(nullptr) {}// 添加设备void addDevice(int id, const std::string& type, const std::string& address) {Device* newDevice = new Device(id, type, address);if (!head) {head = newDevice;} else {newDevice->next = head;head->prev = newDevice;head = newDevice;}}// 删除设备void removeDevice(int id) {Device* current = head;while (current) {if (current->deviceId == id) {if (current->prev) {current->prev->next = current->next;} else {head = current->next;}if (current->next) {current->next->prev = current->prev;}delete current;break;}current = current->next;}}// 遍历设备列表void printDevices() {Device* current = head;while (current) {std::cout << "Device ID: " << current->deviceId<< ", Type: " << current->deviceType<< ", Address: " << current->communicationAddress<< ", Connected: " << (current->isConnected ? "Yes" : "No") << std::endl;current = current->next;}}// 查找设备Device* findDevice(int id) {Device* current = head;while (current) {if (current->deviceId == id) {return current;}current = current->next;}return nullptr;}
};
5.1.4 代码解释
Device 结构体
  • 整体构思设计:在这个组态软件设备管理的场景中,我们需要一个数据结构来代表每一个具体的工业设备。Device 结构体就承担了这个角色,它将设备的关键属性和用于构建双向链表的指针封装在一起,使得每个设备节点可以方便地连接到链表中,便于对设备进行统一管理。
  • 各成员变量说明
    • int deviceId:这是设备的唯一标识符,就像设备的身份证号码一样,在整个系统中每个设备的 deviceId 都是独一无二的。通过这个 ID,我们可以准确地定位和区分不同的设备,方便进行设备的查找、删除等操作。
    • std::string deviceType:用于记录设备的类型,比如传感器、电机、阀门等。不同类型的设备可能具有不同的功能和操作方式,这个属性可以帮助我们在处理设备时进行分类和区分。
    • std::string communicationAddress:表示设备的通信地址,组态软件通过这个地址与设备进行通信,获取设备的状态信息或者向设备发送控制指令。它可以是 IP 地址、串口地址等具体的通信标识。
    • bool isConnected:用于标记设备的连接状态,true 表示设备已连接到系统,false 表示设备未连接。通过这个状态,我们可以快速了解设备的工作情况,及时发现设备连接异常。
    • Device* prevDevice* next:这两个指针是构建双向链表的关键。prev 指针指向前一个设备节点,next 指针指向后一个设备节点。通过这两个指针,我们可以在链表中方便地进行双向遍历,从一个节点快速访问到它的前一个或后一个节点。
  • 构造函数 Device(int id, const std::string& type, const std::string& address):该构造函数用于初始化 Device 结构体的各个成员变量。当创建一个新的设备节点时,我们需要传入设备的 ID、类型和通信地址,构造函数会将这些值赋给相应的成员变量,并将 isConnected 初始化为 false,表示设备初始状态为未连接,同时将 prevnext 指针初始化为 nullptr,表示该节点暂时没有前后连接的节点。
DeviceList
  • 整体构思设计:这个类是对设备链表的管理类,它封装了对设备链表的各种操作,如添加设备、删除设备、遍历设备列表和查找设备等。通过这个类,我们可以方便地对整个设备链表进行统一管理,而不需要直接操作链表节点的指针,提高了代码的可读性和可维护性。
  • 成员变量 Device* head:这是一个指向链表头节点的指针,它是整个设备链表的入口。通过 head 指针,我们可以访问链表中的第一个设备节点,进而遍历整个链表。当链表为空时,head 指针为 nullptr
  • 构造函数 DeviceList():在创建 DeviceList 对象时,该构造函数会将 head 指针初始化为 nullptr,表示此时设备链表为空,没有任何设备节点。
addDevice(int id, const std::string& type, const std::string& address) 方法
  • 功能设计:该方法用于向设备链表中添加一个新的设备节点。
  • 实现思路
    1. 首先,根据传入的设备 ID、类型和通信地址创建一个新的 Device 对象 newDevice
    2. 然后判断链表是否为空,即检查 head 指针是否为 nullptr。如果链表为空,说明这是第一个添加的设备节点,直接将 head 指针指向 newDevice
    3. 如果链表不为空,将新节点插入到链表的头部。具体操作是将 newDevicenext 指针指向当前的头节点 head,同时将当前头节点 headprev 指针指向 newDevice,最后更新 head 指针为 newDevice,这样新节点就成为了新的头节点。
removeDevice(int id) 方法
  • 功能设计:该方法用于从设备链表中删除指定 ID 的设备节点。
  • 实现思路
    1. 初始化一个指针 current 指向链表的头节点 head,从链表的第一个节点开始遍历。

相关文章:

双向链表在系统调度、游戏、文本编辑及组态方面的应用

在编程的奇妙世界里&#xff0c;数据结构就像是一把把神奇的钥匙&#xff08;前面我们介绍过单向链表的基础了&#xff0c;这里我们更进一步&#xff09;&#xff0c;能帮我们打开解决各种问题的大门。今天&#xff0c;咱们就来聊聊其中一把特别的钥匙——双向链表。双向链表和…...

实践网络安全:常见威胁与应对策略详解

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 引言 在数字化转型的浪潮中&#xff0c;网络安全的重要性已达到前所未有的高度。无论是个人用户、企业&#xff0c;还是政府机构…...

关于2024年

关于2024年 十分钟前我从床上爬起来&#xff0c;坐在电脑面前先后听了《黄金时代》——声音碎片和《Song F》——达达两首歌&#xff0c;我觉得躺着有些无聊&#xff0c;又或者除夕夜的晚上躺着让我觉得有些不适&#xff0c;我觉得自己应该爬起来&#xff0c;爬起来记录一下我…...

Hive:Hive Shell技巧

在终端命令窗口不能直接执行select,creat等HQL命令,需要先进入hive之后才能执行,比较麻烦,但是如果使用Hive Shell就可以直接执行 在终端只执行一次Hive命令 -e 参数, "execute"&#xff08;执行&#xff09;,使用-e参数后会在执行完Hive的命令后退出Hive 使用场景:…...

Markdown Viewer 浏览器, vscode

使用VS Code插件打造完美的MarkDown编辑器&#xff08;插件安装、插件配置、markdown语法&#xff09;_vscode markdown-CSDN博客 右键 .md 文件&#xff0c;选择打开 方式 &#xff08;安装一些markdown的插件) vscode如何预览markdown文件 | Fromidea GitCode - 全球开发者…...

快速分析LabVIEW主要特征进行判断

在LabVIEW中&#xff0c;快速分析程序特征进行判断是提升开发效率和减少调试时间的重要技巧。本文将介绍如何高效地识别和分析程序的关键特征&#xff0c;从而帮助开发者在编写和优化程序时做出及时的判断&#xff0c;避免不必要的错误。 ​ 数据流和并行性分析 LabVIEW的图形…...

【Super Tilemap Editor使用详解】(十五):从 TMX 文件导入地图(Importing from TMX files)

Super Tilemap Editor 支持从 TMX 文件(Tiled Map Editor 的文件格式)导入图块地图。通过导入 TMX 文件,你可以将 Tiled 中设计的地图快速转换为 Unity 中的图块地图,并自动创建图块地图组(Tilemap Group)。以下是详细的导入步骤和准备工作。 一、导入前的准备工作 在导…...

JavaScript系列(45)--响应式编程实现详解

JavaScript响应式编程实现详解 &#x1f504; 今天&#xff0c;让我们深入探讨JavaScript的响应式编程实现。响应式编程是一种基于数据流和变化传播的编程范式&#xff0c;它使我们能够以声明式的方式处理异步数据流。 响应式编程基础概念 &#x1f31f; &#x1f4a1; 小知识…...

Lustre Core 语法 - 布尔表达式

Lustre v6 中的 Lustre Core 部分支持的表达式种类中&#xff0c;支持布尔表达式。相关的表达式包括and, or, xor, not, #, nor。 相应的文法定义为 Expression :: not Expression| Expression and Expression| Expression or Expression | Expression xor Expression | # (…...

python学opencv|读取图像(四十六)使用cv2.bitwise_or()函数实现图像按位或运算

【0】基础定义 按位与运算&#xff1a;全1取1&#xff0c;其余取0。按位或运算&#xff1a;全0取0&#xff0c;其余取1。 【1】引言 前序学习进程中&#xff0c;已经对图像按位与计算进行了详细探究&#xff0c;相关文章链接如下&#xff1a; python学opencv|读取图像&…...

C# 添加、替换、提取、或删除Excel中的图片

在Excel中插入与数据相关的图片&#xff0c;能将关键数据或信息以更直观的方式呈现出来&#xff0c;使文档更加美观。此外&#xff0c;对于已有图片&#xff0c;你有事可能需要更新图片以确保信息的准确性&#xff0c;或者将Excel 中的图片单独保存&#xff0c;用于资料归档、备…...

工作总结:压测篇

前言 压测是测试需要会的一项技能&#xff0c;作为开发&#xff0c;有点时候也要会一点压测。也是被逼着现学现卖的。 一、压测是什么&#xff0c;以及压测工具的选择 压测&#xff0c;即压力测试&#xff0c;是一种性能测试手段&#xff0c;通过模拟大量用户同时访问系统&am…...

11JavaWeb——SpringBootWeb案例02

前面我们已经实现了员工信息的条件分页查询以及删除操作。 关于员工管理的功能&#xff0c;还有两个需要实现&#xff1a; 新增员工 修改员工 首先我们先完成"新增员工"的功能开发&#xff0c;再完成"修改员工"的功能开发。而在"新增员工"中…...

vs2022+tesseract ocr识别中英文 编译好的库下载

测试图片 效果 编译其实挺麻烦的&#xff0c;可参考&#xff1a;在Windows上用Visual Studio编译Tesseract_windows编译tesseract-CSDN博客 #include "baseapi.h" #include "allheaders.h" #include <iostream> #include <fstream> // 用于文…...

状态模式——C++实现

目录 1. 状态模式简介 2. 代码示例 3. 单例状态对象 4. 状态模式与策略模式的辨析 1. 状态模式简介 状态模式是一种行为型模式。 状态模式的定义&#xff1a;状态模式允许对象在内部状态改变时改变它的行为&#xff0c;对象看起来好像修改了它的类。 通俗的说就是一个对象…...

3.观察者模式(Observer)

组件协作模式 现代软件专业分工之后的第一个结果是 “框架与应用程序的划分”,“组件协作” 模式通过晚期绑定&#xff0c;来实现框架与应用程序直接的松耦合&#xff0c;是二者之间协作时常用的模式 典型模式 Template Method Strategy Observer /Event 动机&#xff08;M…...

Kotlin判空辅助工具

1&#xff09;?.操作符 //执行逻辑 if (person ! null) {person.doSomething() } //表达式 person?.doSomething() 2&#xff09;?:操作符 //执行逻辑 val c if (a ! null) {a } else {b } //表达式 val c a ?: b 3&#xff09;!!表达式 var message: String? &qu…...

Electron学习笔记,安装环境(1)

1、支持win7的Electron 的版本是18&#xff0c;这里node.js用的是14版本&#xff08;node-v14.21.3-x86.msi&#xff09;云盘有安装包 Electron 18.x (截至2023年仍在维护中): Chromium: 96 Node.js: 14.17.0 2、安装node环境&#xff0c;node-v14.21.3-x86.msi双击运行选择安…...

将 OneLake 数据索引到 Elasticsearch - 第 1 部分

作者&#xff1a;来自 Elastic Gustavo Llermaly 学习配置 OneLake&#xff0c;使用 Python 消费数据并在 Elasticsearch 中索引文档&#xff0c;然后运行语义搜索。 OneLake 是一款工具&#xff0c;可让你连接到不同的 Microsoft 数据源&#xff0c;例如 Power BI、Data Activ…...

【C++】STL介绍 + string类使用介绍 + 模拟实现string类

目录 前言 一、STL简介 二、string类 1.为什么学习string类 2.标准库中的string类 3.auto和范围for 4.迭代器 5.string类的常用接口说明 三、模拟实现 string类 前言 本文带大家入坑STL&#xff0c;学习第一个容器string。 一、STL简介 在学习C数据结构和算法前&#xff0c;我…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...