STL c++ list——模擬實現

結點類的模擬實現

list是一個帶頭雙向循環鏈表

因需要實現一個節點類,其中包含哨兵位(用來標識位置),節點信息(val數據,prev后指針,next后指針)

template<class T>
struct list_node
{T _data;list_node<T>* _next;//指向下一個list_node<T>* _prev;//上一個list_node(const T& x=T()):_data(x),_next(nullptr),_prev(nullptr){}
};

知識點:構造函數

  • 在后面要new開空間,需要構造分為要帶參和不帶的?--》全缺省 T()給個缺省值應對不同類型
  • 為什么用struct--》當類中全部都是公有,一般用struct (其實和class沒什么區別)

迭代器類的模擬實現

模擬迭代器的意義

之前模擬實現string和vector時都沒有說要實現一個迭代器類,為什么實現list的時候就需要實現一個迭代器類了呢?

string和vector

因為string和vector對象都將其數據存儲在了一塊連續的內存空間,我們通過指針進行自增、自減以及解引用等操作,就可以對相應位置的數據進行一系列操作,因此string和vector當中的迭代器就是原生指針。

list?

list來說,其各個結點在內存當中的位置是隨機的,并不是連續的,我們不能僅通過結點指針的自增、自減以及解引用等操作對相應結點的數據進行操作。

?

總結: list迭代器類,實際上就是對結點指針進行了封裝,對其各種運算符進行了重載。?

?普通迭代器

構造函數

// 構造函數,接收一個節點指針,用于初始化迭代器,使其指向傳入的節點list_iterator(Node* node) :_node(node) {} 

前置 ++ 運算符重載(operator++()?)

前置 ++ 原本的作用是將數據自增,然后返回自增后的數據。對于鏈表迭代器的前置 ++,目的是讓迭代器指向下一個節點并返回修改后的迭代器自身。

// 前置++
Self& operator++() 
{_node = _node->_next; // 讓結點指針指向下一個結點return *this; // 返回自增后的迭代器自身引用
}

此函數先讓迭代器指向的節點指針移動到下一個節點,再返回修改后的迭代器自身引用。

前置 -- 運算符重載(operator--()?)

前置 -- 原本的作用是將數據自減,然后返回自減后的數據。對于鏈表迭代器的前置 --,要讓迭代器指向前一個節點并返回修改后的迭代器自身。

// 前置--
Self& operator--() 
{_node = _node->_prev; // 讓結點指針指向前一個結點return *this; // 返回自減后的迭代器自身引用
}

該函數讓迭代器的節點指針移動到前一個節點,再返回移動后的迭代器自身引用。

后置 ++ 運算符重載(operator++(int)?)

后置 ++ 原本是先返回數據原來的值,然后再將數據自增。對于鏈表迭代器的后置 ++,需先記錄當前迭代器狀態,再讓迭代器移動到下一個節點,最后返回原始狀態的迭代器。

// 后置++
Self operator++(int) 
{Self tmp(*this); // 記錄當前結點指針的指向,也就是記錄迭代器當前狀態_node = _node->_next; // 讓結點指針指向下一個結點return tmp; // 返回自增前的迭代器,即記錄的原始狀態的迭代器
}

函數先保存迭代器原始狀態,再使其移動到下一個節點,最后返回原始迭代器。

知識點:C++ 規定后置自減運算符重載函數需要帶一個 int 類型的參數,這個參數在函數實現中通常不會被使用,它僅僅是作為一個標記,用于編譯器區分前置和后置運算符。 ?

后置 -- 運算符重載(operator--(int)?)

后置 -- 原本是先返回數據原來的值,然后再將數據自減對于鏈表迭代器的后置 --,要先記錄當前迭代器狀態,再讓迭代器移動到前一個節點,最后返回原始狀態的迭代器。

// 后置--
Self operator--(int) 
{Self tmp(*this); // 記錄當前結點指針的指向,即記錄迭代器當前狀態_node = _node->_prev; // 讓結點指針指向前一個結點return tmp; // 返回自減前的迭代器,即記錄的原始狀態的迭代器
}

此函數先保存迭代器當前狀態,再讓其指向前一個節點,最后返回原始迭代器。

解引用運算符重載(operator*()?)

解引用運算符?*?原本用于獲取指針所指向的數據對于鏈表迭代器的?*?運算符重載,目的是獲取當前所指向節點存儲的數據的引用,以便讀寫。

// 解引用運算符重載
T& operator*() 
{return _node->_data; // 返回當前節點存儲的數據的引用
}

該函數返回當前節點中存儲的數據的引用。

箭頭運算符重載(operator->()?)

箭頭運算符?->?通常用于通過指針訪問結構體或類的成員對于鏈表迭代器的?->?運算符重載,是為了方便訪問當前所指向節點存儲數據的成員。

// 箭頭運算符重載
T* operator->() 
{return &_node->_data; // 返回當前節點存儲數據的指針
}

函數返回當前節點存儲數據的指針,可使用?迭代器->成員?訪問節點數據成員。

不等于運算符重載(operator!=()?)

不等于運算符?!=?用于判斷兩個對象是否不相等。對于鏈表迭代器的?!=?運算符重載,是為了判斷兩個迭代器是否指向不同的節點。

// 不等于運算符重載
bool operator!=(const Self& s) 
{return _node != s._node; // 判斷當前迭代器和傳入迭代器所指向的節點是否不同
}

此函數比較兩個迭代器所指向的節點指針是否不同,不同則返回?true

const迭代器

const迭代器與普通不同點在于要單獨實現個類(const 迭代器負責只讀遍歷)并且在類中的 *, -> 的返回值和其他的不同

  • 普通迭代器:可讀可寫,通過重載的?operator*?和?operator->?返回值無?const?修飾,能修改指向的 list 元素 。比如?*it = newValue;it?為普通迭代器)可修改元素值。

  • const 迭代器:只讀,?operator*?返回?const T&?,operator->?返回?const T*?,禁止通過迭代器修改 list 元素。若?*it = newValue;it?為 const 迭代器),編譯器會報錯。

  • const 迭代器指向的內容不能改變 所以要模擬前置const


?普通類和const類結合


先看編譯器底層

這里我們所實現的迭代器類的模板參數列表當中為什么有三個模板參數?

template<class T, class Ref, class Ptr>

普通迭代器和const迭代器。

typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

這里我們就可以看出,迭代器類的模板參數列表當中的Ref和Ptr分別代表的是引用類型和指針類型。

當我們使用普通迭代器時,編譯器就會實例化出一個普通迭代器對象;當我們使用const迭代器時,編譯器就會實例化出一個const迭代器對象。

// 定義普通迭代器類型
// 這里使用了模板參數T,將list_iterator類模板實例化為普通迭代器,Ref為T&,Ptr為T*,意味著可以對元素進行讀寫操作
typedef list_iterator<T, T&, T*> iterator;
// 定義常量迭代器類型
// 同樣使用模板參數T,但Ref為const T&,Ptr為const T*,表示只能對元素進行只讀操作
typedef list_iterator<T, const T&, const T*> const_iterator;// 定義list_iterator類模板,包含三個模板參數
// T 表示鏈表中存儲的數據類型
// Ref 表示引用類型,用于operator*返回值的類型,普通迭代器時為T&,常量迭代器時為const T&
// Ptr 表示指針類型,用于operator->返回值的類型,普通迭代器時為T*,常量迭代器時為const T*
template<class T, class Ref, class Ptr>
struct list_iterator
{// 定義Node類型為list_node<T>,方便后續使用,list_node<T>應該是鏈表節點的類型typedef list_node<T> Node;// 定義Self類型為list_iterator<T, Ref, Ptr>,方便在類內使用自身類型typedef list_iterator<T, Ref, Ptr> Self;// 指向鏈表節點的指針,用于存儲迭代器當前指向的節點Node* _node;// 構造函數,接收一個節點指針,用于初始化迭代器,使其指向傳入的節點list_iterator(Node* node):_node(node){}// 重載*運算符,返回當前節點存儲的數據的引用// 返回值類型為Ref,根據模板參數不同,普通迭代器時為T&,常量迭代器時為const T&Ref operator*(){return _node->_data;}// 重載->運算符,返回當前節點存儲數據的指針// 返回值類型為Ptr,根據模板參數不同,普通迭代器時為T*,常量迭代器時為const T*Ptr operator->(){return &_node->_data;}// 重載前置++運算符,將迭代器指向下一個節點,并返回修改后的迭代器自身引用Self& operator++(){_node = _node->_next;return *this;}// 重載前置--運算符,將迭代器指向前一個節點,并返回修改后的迭代器自身引用Self& operator--(){_node = _node->_prev;return *this;}// 重載后置++運算符,先保存當前迭代器狀態,然后將迭代器指向下一個節點,最后返回保存的原始迭代器Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}// 重載后置--運算符,先保存當前迭代器狀態,然后將迭代器指向前一個節點,最后返回保存的原始迭代器Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}// 重載!=運算符,用于比較兩個迭代器是否指向不同的節點// 返回true表示兩個迭代器指向不同節點,否則返回falsebool operator!=(const Self& s){return _node != s._node;} // 重載==運算符,用于比較兩個迭代器是否指向相同的節點// 返回true表示兩個迭代器指向相同節點,否則返回falsebool operator==(const Self& s){return _node == s._node;}
};

知識點:對于->

當list容器當中的每個結點存儲的不是內置類型,而是自定義類型,例如日期類,那么當我們拿到一個位置的迭代器時,我們可能會使用->運算符訪問Date的成員:

	list<Date> lt;Date d1(2021, 8, 10);Date d2(1980, 4, 3);Date d3(1931, 6, 29);lt.push_back(d1);lt.push_back(d2);lt.push_back(d3);list<Date>::iterator pos = lt.begin();cout << pos->_year << endl; //輸出第一個日期的年份
//相當于cout << pos.operator->()->_year << endl;

對于->運算符的重載,我們直接返回結點當中所存儲數據的地址即可。

從邏輯上,原本應該是先調用重載的?operator->?得到自定義類型(如?Date?)的指針?pd?,然后再用這個指針?pd?去訪問成員變量,即?pd->year?,也就是理論上會出現?pos->->year?這種形式 。但 C++ 語法規定,當重載了?->?運算符返回一個指針后,編譯器會自動處理,使得我們可以直接寫?pos->year?,省略掉第二個?->?,編譯器會根據重載規則理解為先用?pos->?調用重載函數得到指針,再用指針訪問成員 。

?

默認成員函數

  • 構造函數

list 是一個帶頭雙向循環鏈表。在構造一個 list 對象時,直接申請一個頭結點,并讓其前驅指針和后繼指針都指向自己。這樣就構建好了一個空鏈表的初始結構。

// 構造函數
list()
{_head = new Node;_head->_next = _head;_head->_prev = _head;
}
  • 拷貝構造函數

拷貝構造函數的作用是根據所給 list 容器,構造出一個新對象。首先申請一個頭結點并完成初始化,然后遍歷原容器,將其中的數據逐個尾插到新構造的容器中。

// 拷貝構造函數
list(const list<T>& lt)
{_head = new Node;_head->_next = _head;_head->_prev = _head;for (auto it = lt.begin(); it != lt.end(); ++it){push_back(*it);}
}
  1. 賦值運算符重載函數

  • 寫法一:傳統寫法
    先調用 clear 函數將原容器清空,避免殘留數據干擾。然后遍歷傳入容器,將其中的數據逐個尾插到清空后的容器中,實現賦值操作。

// 傳統寫法
list<T>& operator=(const list<T>& lt)
{if (this != &lt){clear();for (auto it = lt.begin(); it != lt.end(); ++it){push_back(*it);}}return *this;
}
  • 寫法二:現代寫法
    利用編譯器機制,故意不使用引用接收參數,讓編譯器自動調用拷貝構造函數構造出一個臨時 list 對象。然后調用 swap 函數將原容器與該臨時對象進行交換,實現高效賦值。

// 現代寫法
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}

析構函數
對象析構時,先調用 clear 函數清理容器中的數據,釋放所有有效節點。接著釋放頭結點,最后將頭指針置空,完成資源的徹底釋放。

// 析構函數
~list()
{clear();delete _head;_head = nullptr;
}

迭代器相關函數

begin 和 end

  • 對于普通 list 對象:
    begin 函數返回第一個有效數據的迭代器,即使用頭結點后一個結點的地址構造出來的迭代器;end 函數返回最后一個有效數據的下一個位置的迭代器,也就是頭結點地址構造的迭代器。

iterator begin()
{return iterator(_head->_next);
}
iterator end()
{return iterator(_head);
}
  • 對于 const list 對象:
    重載 const 版本的 begin 和 end 函數,返回 const 迭代器,保證在遍歷 const 對象時不能修改數據。

const_iterator begin() const
{return const_iterator(_head->_next);
}
const_iterator end() const
{return const_iterator(_head);
}

訪問容器相關函數

front 和 back

對于普通 list 對象:
front 函數返回第一個有效數據的引用,通過解引用 begin 函數返回的迭代器實現;back 函數返回最后一個有效數據的引用,通過先將 end 函數返回的迭代器前置遞減,再解引用實現。

T& front()
{return *begin();
}
T& back()
{return *(--end());
}
  • 對于 const list 對象:
    重載 const 版本的 front 和 back 函數,返回 const 引用,防止通過這些函數修改 const 對象中的數據。

const T& front() const
{return *begin();
}
const T& back() const
{return *(--end());
}

插入、刪除函數

insert


insert 函數可以在所給迭代器之前插入一個新結點。先根據迭代器得到對應位置的結點指針 cur,再找到 cur 的前驅結點指針 prev,根據給定數據構造待插入結點 newnode,然后建立 newnode 與 cur、prev 之間的雙向鏈接關系。

iterator insert(iterator pos, const T& x)
{assert(pos._node);Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);
}

?

erase


erase 函數用于刪除所給迭代器位置的結點。先根據迭代器得到對應位置的結點指針 cur,以及其前驅結點指針 prev 和后繼結點指針 next,釋放 cur 結點,再建立 prev 和 next 之間的雙向鏈接關系,最后返回刪除位置的下一個迭代器。

// erase函數
// 功能:刪除所給迭代器pos位置的結點
// 參數:iterator pos,指定要刪除結點位置的迭代器
// 返回值:iterator,返回指向刪除位置下一個結點的迭代器
iterator erase(iterator pos)
{// 確保迭代器pos有效assert(pos._node);// 確保要刪除的不是end迭代器指向的位置(頭結點)assert(pos != end());// 獲取迭代器pos指向的結點指針Node* cur = pos._node;// 獲取cur結點的前驅結點指針Node* prev = cur->_prev;// 獲取cur結點的后繼結點指針Node* next = cur->_next;// 建立prev和next結點之間的雙向鏈接關系prev->_next = next;next->_prev = prev;// 釋放要刪除的cur結點delete cur;// 返回指向刪除位置下一個結點的迭代器return iterator(next);
}

push_back 和 pop_back

  • push_back 函數在鏈表尾部插入數據,復用 insert 函數,在 end 迭代器位置前插入元素。

void push_back(const T& x)
{insert(end(), x);
}
  • pop_back 函數刪除鏈表尾部元素,復用 erase 函數,刪除 end 迭代器前置遞減位置的元素。

void pop_back()
{assert(!empty());erase(--end());
}

push_front 和 pop_front

  • push_front 函數在鏈表頭部插入數據,復用 insert 函數,在 begin 迭代器位置前插入元素。

void push_front(const T& x)
{insert(begin(), x);
}
  • pop_front 函數刪除鏈表頭部元素,復用 erase 函數,刪除 begin 迭代器位置的元素。

void pop_front()
{assert(!empty());erase(begin());
}

其他函數

size


size 函數用于獲取當前容器中的有效數據個數。由于 list 是鏈表結構,只能通過遍歷的方式逐個統計元素個數。

size_t size() const
{size_t count = 0;for (auto it = begin(); it != end(); ++it){++count;}return count;
}

empty


empty 函數用于判斷容器是否為空,通過比較 begin 和 end 迭代器是否相等來判斷,相等則表示容器為空。

bool empty() const
{return begin() == end();
}

clear


clear 函數用于清空容器中的數據。從第一個有效數據結點開始,逐個刪除結點,直到只剩下頭結點,完成數據清理。

void clear()
{auto it = begin();while (it != end()){auto next = it;++next;delete it._node;it = next;}_head->_next = _head;_head->_prev = _head;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77153.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77153.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77153.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ORM、Mybatis和Hibernate、Mybatis使用教程、parameterType、resultType、級聯查詢案例、resultMap映射

DAY21.1 Java核心基礎 ORM Object Relationship Mapping 對象關系映射 面向對象的程序到—關系型數據庫的映射 比如java – MySQL的映射 ORM框架就是實現這個映射的框架 Hibernate、Mybatis、MybatisPlus、Spring Data JPA、Spring JDBC Spring Data JPA的底層就是Hiber…

【學習自用】配置文件中的配置項

server.port服務器端口&#xff0c;常被用于指定應用程序運行時所監聽的端口號spring.datasource.url用于配置數據源的數據庫連接URLspring.datasource.username用于指定連接數據庫的用戶名spring.datasource.password用于配置數據源時設置數據庫連接密碼的屬性mybatis.mapper-…

使用protobuf編譯提示無法打開包括文件: ‘absl/log/absl_log.h’: No such file or directory

問題原因 Protobuf 依賴 Abseil&#xff1a; Protobuf 3.20 版本開始依賴 Abseil&#xff0c;但你的系統未正確安裝或配置 Abseil。 頭文件路徑未包含&#xff1a; 編譯器找不到 absl/log/absl_log.h&#xff0c;可能是因為 Abseil 未正確安裝或未在項目中設置包含路徑。 …

Spring AI Alibaba 文檔檢索使用

一、文檔檢索 (Document Retriever)簡介 1、核心概念 文檔檢索&#xff08;DocumentRetriever&#xff09;是一種信息檢索技術&#xff0c;旨在從大量未結構化或半結構化文檔中快速找到與特定查詢相關的文檔或信息。文檔檢索通常以在線(online)方式運行。 DocumentRetriever通…

前端面試核心知識點整理:從 JavaScript 到 Vue 全解析

一、JavaScript 異步編程核心:Promise 與 async/await 1. Promise 深度解析 定義:Promise 是處理異步操作的對象,代表一個異步操作的最終狀態(成功 / 失敗)。三種狀態: pending(進行中):初始狀態,異步操作未完成。fulfilled(已成功):異步操作成功,調用 resolve …

音視頻(四)android編譯

前言 前面已經講了在windows上應用了&#xff0c;這章主要講述android上編譯 1&#xff1a;環境 git 如果失敗 直接跑到相應網站 手動下載 ubuntu22.* android ndk r21e download:https://developer.android.google.cn/ndk/downloads/index.html?hluk 為什么用這個&#xff0…

【kind管理腳本-3】腳本函數說明文檔 —— 便捷使用 kind 創建、刪除、管理集群腳本

下面是一份詳細的說明文檔&#xff0c;介紹該腳本的功能、用法及各部分的含義&#xff0c;供您參考和使用&#xff1a; Kind 集群管理腳本說明文檔 此腳本主要用于管理 Kind&#xff08;Kubernetes IN Docker&#xff09;集群&#xff0c;提供創建、刪除、導出 kubeconfig、加…

【計算機行業發展與重塑】

計算機行業正經歷前所未有的變革&#xff0c;AI技術的爆發式發展與產業升級的深度融合&#xff0c;正在重塑行業格局與就業市場。以下從行業趨勢、AI的核心價值、就業需求三個維度展開分析。 一、行業趨勢&#xff1a;AI驅動下的多極增長 AI成為核心引擎 生成式AI的突破&#…

(高頻SQL50題)1667. 修復表中的名字

問題 表&#xff1a; Users ------------------------- | Column Name | Type | ------------------------- | user_id | int | | name | varchar | ------------------------- user_id 是該表的主鍵(具有唯一值的列)。 該表包含用戶的 ID 和名字…

基于人工智能的醫學影像關聯分析:利用潛在空間幾何混雜因素校正法|文獻速遞-深度學習醫療AI最新文獻

Title 題目 AI-based association analysis for medical imaging using latent-spacegeometric confounder correction 基于人工智能的醫學影像關聯分析&#xff1a;利用潛在空間幾何混雜因素校正法 01 文獻速遞介紹 人工智能&#xff08;AI&#xff09;已成為各個領域的…

開源免費虛擬化軟件PVE功能介紹

Proxmox VE&#xff08;PVE&#xff09;提供了一個基于 Web UI&#xff08;管理界面&#xff09;的虛擬化管理平臺&#xff0c;用戶可以通過瀏覽器管理 虛擬機&#xff08;VM&#xff09;、容器&#xff08;LXC&#xff09;、存儲、網絡、備份、用戶權限等。 一、PVE Web 界面…

新球體育比分狀態監控

文章目錄 目標分析監控邏輯代碼目標分析 網頁監控地址:aHR0cHM6Ly9saXZlLnRpdGFuMDA3LmNvbS9pbmRleDJpbjEuYXNweD9pZD0x 監控邏輯 比分等數據主要是依賴JS加載得到,通過ajax后端進行渲染 代碼 # -*- coding: utf-8 -*-import warnings warnings.filterwarnings(ignore) f…

【lodash的omit函數詳解 - 從入門到精通】

lodash的omit函數詳解 - 從入門到精通 小白視角&#xff1a;什么是omit&#xff1f; omit在英文中意為"忽略"或"省略"。在編程中&#xff0c;它就是從一個對象中刪除不需要的屬性&#xff0c;返回一個新對象。 // 原始對象 const person {name: "…

軟考筆記9——數據庫技術基礎

第九章節——數據庫技術基礎 數據庫技術基礎 第九章節——數據庫技術基礎一、基本概念1. 數據庫與數據庫系統2. 數據庫的三級模式2.1 內模式2.2 概念模式2.3 外模式2.4 數據庫的兩級映射2.5 數據庫設計的基本步驟 二、數據模型1. 基本概念2. E-R模型2.1 實體2.2 聯系2.3 屬性 3…

Django分頁教程及示例

推薦超級課程: 本地離線DeepSeek AI方案部署實戰教程【完全版】Docker快速入門到精通Kubernetes入門到大師通關課AWS云服務快速入門實戰目錄 完整代碼示例:結論Django的分頁模塊允許你將大量數據分割成更小的塊(頁面)。這對于以可管理的方式顯示項目列表,如博客文章或產品…

int 與 Integer 的區別詳解

1. 本質區別 特性intInteger類型基本數據類型&#xff08;Primitive&#xff09;包裝類&#xff08;Wrapper Class&#xff09;存儲位置棧&#xff08;或作為對象成員在堆中&#xff09;堆&#xff08;對象實例&#xff09;默認值0null&#xff08;可能導致 NullPointerExcept…

mariadb使用docker compose方式安裝

問題 本地mac m1上面的mysql和mariadb突然不用使用了&#xff0c;重新安裝也不想&#xff0c;最近mac系統也更新了&#xff0c;brew也更新了&#xff0c;重新安裝mariadb還是不能正常使用&#xff0c;現在我打算使用docker來安裝本地的mariadb了。 默認配置文件my.cnf 從容器…

基于React + Antd + Java的OFD文件上傳預覽實現方案(OFD文件轉圖片)

一、前端實現方案(React + Antd) import React, {useState } from react; import {Upload, Button, Image, Carousel } from antd; import {UploadOutlined } from @ant-design/icons;const OFDUploadPreview = () => {const [previewImages, setPreviewImages] = useSta…

從零構建大語言模型全棧開發指南:第四部分:工程實踐與部署-4.3.1LangChain與Dify平臺實戰:從RAG到Agent工作流

?? 點擊關注不迷路 ?? 點擊關注不迷路 ?? 點擊關注不迷路 文章大綱 LangChain與Dify平臺實戰:從RAG到Agent工作流 - 4.3.1 LangChain與Dify平臺實戰:從RAG到Agent工作流1. LangChain核心組件與RAG架構設計1.1 LangChain核心模塊1.2 RAG架構實現流程2. RAG實戰:企業知識…

操作 Office Excel 文檔類庫Excelize

Excelize 是 Go 語言編寫的一個用來操作 Office Excel 文檔類庫&#xff0c;基于 ECMA-376 OOXML 技術標準。可以使用它來讀取、寫入 XLSX 文件&#xff0c;相比較其他的開源類庫&#xff0c;Excelize 支持操作帶有數據透視表、切片器、圖表與圖片的 Excel 并支持向 Excel 中插…