【c++】【STL】list詳解

目錄

  • list的作用
  • list的接口
    • 構造函數
    • 賦值運算符重載
    • 迭代器相關
    • size
    • empty
    • front
    • back
    • assign
    • push_front
    • pop_front
    • push_back
    • pop_back
    • insert
    • erase
    • swap
    • resize
    • clear
    • splice
    • remove
    • remove_if
    • unique
    • merge
    • sort
    • reverse
    • 關系運算符重載(非成員函數)
  • list的模擬實現
    • 結點類
    • 迭代器類
      • typedef的妙用
      • *運算符重載
      • ->運算符重載
      • ++、--運算符重載
    • list類
      • CreateHead(頭節點創建)
      • insert
      • erase
      • 賦值運算符重載

list的作用

list是c++的stl庫提供的鏈表容器,鏈表作為我們熟知的數據結構之一,其與順序表相比,在任意位置插入刪除方面具有絕對的優勢,但在隨機讀取方面不如順序表,兩者屬于互補關系,stl中的list底層是用雙向鏈表實現的,以實現反向迭代器的反向讀取,stl中還有forward_list,它是由單向鏈表實現的。

list的接口

構造函數

//默認構造
explicit list (const allocator_type& alloc = allocator_type());
//指定鏈表初始化大小和鏈表初始化的值
explicit list (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
//使用迭代器進行構造           
template <class InputIterator>list (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
//拷貝構造
list (const list& x);

四種構造方式都較為常用

賦值運算符重載

list& operator= (const list& x);

重載了來鏈表復制,代碼更加易讀。

迭代器相關

      iterator begin();
const_iterator begin() const;iterator end();
const_iterator end() const;reverse_iterator rbegin();
const_reverse_iterator rbegin() const;reverse_iterator rend();
const_reverse_iterator rend() const;const_iterator cbegin() const noexcept;const_iterator cend() const noexcept;const_reverse_iterator crbegin() const noexcept;const_reverse_iterator crend() const noexcept;

由于鏈表是非連續性容器,所以迭代器還是很實用的。

size

size_type size() const;

返回鏈表尺寸。

empty

bool empty() const;

鏈表判空。

front

      reference front();
const_reference front() const;

返回對鏈表第一個元素的引用。

back

      reference back();
const_reference back() const;

返回對鏈表最后一個元素的引用。

assign

template <class InputIterator>void assign (InputIterator first, InputIterator last);//迭代器初始化
void assign (size_type n, const value_type& val);//指定初始化大小和內容

重新初始化鏈表。

push_front

void push_front (const value_type& val);

頭插。

pop_front

void pop_front();

頭刪。

push_back

void push_back (const value_type& val);

尾插。

pop_back

void pop_back();

尾刪。

insert

//插入一個
iterator insert (iterator position, const value_type& val);//插入一段
void insert (iterator position, size_type n, const value_type& val);//插入迭代器表示的一段
template <class InputIterator>void insert (iterator position, InputIterator first, InputIterator last);

插入函數,鏈表的插入函數效率很高。

erase

iterator erase (iterator position);//刪除一個
iterator erase (iterator first, iterator last);//刪除一段

刪除函數,鏈表的刪除函數效也很高。

swap

void swap (list& x);

交換函數,交換兩個鏈表。

resize

void resize (size_type n, value_type val = value_type());

重新指定鏈表的大小,如果n大于鏈表此時的size,就擴充到n大小,然后將擴充后的節點的值初始化成指定內容(缺省為0);如果n小于鏈表此時的size,就減小到n大小,然后刪除并銷毀超出的部分。

clear

void clear();

清空鏈表。

splice

void splice (iterator position, list& x);//轉移一整個
void splice (iterator position, list& x, iterator i);//轉移指定位置的元素
void splice (iterator position, list& x, iterator first, iterator last);//轉移指定的一段。

將鏈表中的元素轉移到另一個鏈表中,position是插入位置。

remove

void remove (const value_type& val);

刪除鏈表中所有與給定值相等的元素。

remove_if

template <class Predicate>void remove_if (Predicate pred);

刪除鏈表中所有Predicate pred返回true的元素的函數,這允許我們刪除滿足特定復雜條件的元素。

unique

void unique();
template <class BinaryPredicate>void unique (BinaryPredicate binary_pred);

這個函數可以讓鏈表中等于特定值或滿足特定條件的元素唯一,與remove和remove_if函數類似,只不過他們是全部刪除,unique是要留一個。

merge

  void merge (list& x);
template <class Compare>void merge (list& x, Compare comp);

將兩個已經滿足一定順序的鏈表合并為一個滿足這個順序的新鏈表,默認順序是升序,也就是說合并兩個升序鏈表時可以用第一個函數,其他的都需要寫仿函數。

sort

void sort();默認升序
template <class Compare>void sort (Compare comp);//自定義

list自己的排序函數。

reverse

void reverse();

list自己的反轉函數。

關系運算符重載(非成員函數)

template <class T, class Alloc>bool operator== (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);
template <class T, class Alloc>bool operator!= (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);
template <class T, class Alloc>bool operator<  (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);
template <class T, class Alloc>bool operator<= (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);
template <class T, class Alloc>bool operator>  (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);
template <class T, class Alloc>bool operator>= (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);

list的模擬實現

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;namespace jiunian
{// List的節點類template<class T>struct ListNode{ListNode(const T& val = T()) :_pPrev(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPrev;ListNode<T>* _pNext;T _val;};//List的迭代器類template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(PNode pNode = nullptr):_pNode(pNode){}ListIterator(const Self& l):_pNode(l._pNode){}Ref operator*(){return _pNode->_val;}Ptr operator->(){return &(_pNode->_val);}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self& operator++(int){Self tmp(*this);_pNode = _pNode->_pNext;return tmp;}Self& operator--(){_pNode = _pNode->_pPrev;return *this;}Self& operator--(int){Self tmp(*this);_pNode = _pNode->_pPrev;return tmp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}PNode _pNode;};//list類template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;public:///// List的構造 list(){CreateHead();}list(int n, const T& value = T()){CreateHead();while(n--){push_back(value);}}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*(first++));}}list(const list<T>& l){CreateHead();for (auto& e : l){push_back(e);}}list<T>& operator=(list<T> l){swap(l);return *this;}~list(){clear();delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return _pHead->_pNext;}iterator end(){return _pHead;}const_iterator begin()const{return _pHead->_pNext;}const_iterator end()const{return _pHead;}///// List Capacitysize_t size()const{return _size;}bool empty()const{return _pHead == _pHead->_pNext;//size == 0;}// List AccessT& front(){return _pHead->_pNext->_val;}const T& front()const{return _pHead->_pNext->_val;}T& back(){return _pHead->_pPrev->_val;}const T& back()const{return _pHead->_pPrev->_val;}// List Modifyvoid push_back(const T& val){ insert(end(), val); }void pop_back() {erase(--end()); }void push_front(const T& val){insert(begin(), val); }void pop_front() {erase(begin()); }// 在pos位置前插入值為val的節點iterator insert(iterator pos, const T& val){Node* newnode = new Node(val);newnode->_pPrev = pos._pNode->_pPrev;newnode->_pNext = pos._pNode;pos._pNode->_pPrev->_pNext = newnode;pos._pNode->_pPrev = newnode;++_size;return newnode;}//iterator insert(iterator pos, const T& x)//{//    Node* cur = pos._pNode;//    Node* prev = cur->_pPrev;//    Node* newnode = new Node(x);//    prev->_pNext = newnode;//    newnode->_pNext = cur;//    cur->_pPrev = newnode;//    newnode->_pPrev = prev;//    //++_size;//    return newnode;//}// 刪除pos位置的節點,返回該節點的下一個位置iterator erase(iterator pos){pos._pNode->_pNext->_pPrev = pos._pNode->_pPrev;pos._pNode->_pPrev->_pNext = pos._pNode->_pNext;iterator ret = pos._pNode->_pNext;delete pos._pNode;--_size;return ret;}void clear(){iterator cur = begin();while (cur != end()){cur = erase(cur);}_size = 0;}void swap(list<T>& l){std::swap(_pHead, l._pHead);std::swap(_size, l._size);}private:void CreateHead(){_pHead = new Node;_pHead->_pNext = _pHead;_pHead->_pPrev = _pHead;}PNode _pHead;size_t _size = 0;};
}

對于list這種存儲不連續的容器,其迭代器的實現就不能像string和vector一樣直接對指針進行封裝,雖然對于迭代器來說,其底層的實現離不開指針,但其還是有別于string和vector的。

結點類

在對于迭代器進行說明之前,我還是要先介紹一下鏈表的結點類,結點是組成鏈表的基本單位,是需要單獨封裝的。

// List的節點類
template<class T>
struct ListNode
{ListNode(const T& val = T()) :_pPrev(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPrev;ListNode<T>* _pNext;T _val;
};

由于我們創建的是一個雙向帶頭鏈表,所以一個節點要給一個指針指向前一個結點,也要給一個指針指向后一個節點,再者因為ListNode中的成員之后都要被list類頻繁使用,所以我們直接將類定義成struct,因為struct的元素在不加訪問限定符的情況下都是默認共有的(兼容c語言)。最后我們為這個類寫上構造函數就完成了。

迭代器類

//List的迭代器類
template<class T, class Ref, class Ptr>
struct ListIterator
{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(PNode pNode = nullptr):_pNode(pNode){}ListIterator(const Self& l):_pNode(l._pNode){}Ref operator*(){return _pNode->_val;}Ptr operator->(){return &(_pNode->_val);}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self tmp(*this);_pNode = _pNode->_pNext;return tmp;}Self& operator--(){_pNode = _pNode->_pPrev;return *this;}Self& operator--(int){Self tmp(*this);_pNode = _pNode->_pPrev;return tmp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}PNode _pNode;
};

之后就是迭代器的說明了,在有了對于string和vector的實現經驗之后,其實對于list這個類本身的實現已經很輕松了,因為stl庫中的容器之間是有很強的共性的,實現的思路大差不差,但對于list的迭代器還是有所不同的,因為list要實現不連續內存容器的隨機訪問。首先我們看向這個類所給的模板參數,有三個,這其實是一個令人疑惑的點,因為通常來說我們只需要給一個模板參數說明迭代器指向的節點中的val是什么類型不就行了,但這里所給的參數有足足三個,這里直接理解是理解不同的,我們不妨先往下看。

typedef的妙用

typedef ListNode<T>* PNode;
typedef ListIterator<T, Ref, Ptr> Self;

這兩句代碼我想要單獨拎出來講,如果我們有一些閱讀源碼的經歷,我們會發現寫源碼的那些大佬會經常性的使用typedef,一層套一層,看起來像是脫褲子放屁,但其實不然,比如這里,假如我們不使用typedef,那會阻礙我們書寫代碼嗎?答案是不會的,typedef無非只是個替換,不使用無非只是麻煩一點,多寫幾個模板參數實例化的事(其實省事這一點就足以成為我們使用它的理由了),但倘若我們書寫完代碼之后因為某些原因要改變ListNode或ListIterator的模板參數數量或者模板參數名就麻煩了,我們要把之前寫ListNode和ListIterator都寫一遍,如果代碼量巨大,那將是一場噩夢,但倘若我們用了typedef,不僅書寫時就省了事,書寫之后萬一要對typedef的內容進行更改也是很輕松的,只要把typedef的地方一改其他地方都會改。說到底,這樣寫本質上降低了代碼之間的關聯度(耦合度),我們作為代碼學習者,可能會從某些地方聽說過高內聚低耦合的概念,高內聚低耦合就是指盡量使一個模塊的代碼專注于完成單一任務,且模塊與模塊之間的關聯度盡可能地低。我們這里使用typedef就大大地降低了代碼之間的關聯度,這樣一處代碼的修改帶來的連鎖反應會盡可能地小,這是我們在書寫代碼時要時刻注意的。

*運算符重載

Ref operator*()
{return _pNode->_val;
}

這段代碼是對于*的運算符重載這不難看出,但是我們看想這個函數的返回值,只是我們之前所說的三個模板參數中的第二個,我們仔細想想,倘若這里不使用模板參數,我們應該寫什么呢?當然是_val類型的引用,解引用運算符之后的變量更改會影響原指針指向的數,所以要用引用。但為什么要用模板參數呢,這時我們看向list類的這一段,

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

對于迭代器來說,不僅有普通迭代器,還有const版的,而普通迭代器和const迭代器除了*和->運算符重載不一樣之外,其他的都是一致的,所以我們通過傳三個參數的方式成功偷了一波懶,一個類干了別人兩個類干的事,剩下的事由編譯器來完成。

->運算符重載

Ptr operator->()
{return &(_pNode->_val);
}

這個函數可以訪問_val的對象成員(前提是_val得有對象成員,沒有用不到),這個函數筆者在一開始理解時非常困惑,因為筆者認為迭代器視為指向鏈表結點的指針,而->被用來在使用指針的情況下訪問對象元素,所以這個->是用來訪問系欸但元素的,也就是_val、_pPrev和_pNext,但事實上不是,這里迭代器不應該被看作為一個指向結點的指針,而應該看作為一個指向_val的指針,因為我是在實現這個類的基礎之上去理解這個迭代器的,我先入為主了,最為使用者來說,我并不清楚list類的底層如何實現,我也就不用會知道list其實有一個前置的類叫ListNode,也并不知道ListNode中有三個元素_val、_pPrev和_pNext,在使用者看來,迭代器就是指向容器元素本身,使用->訪問的就是元素這個對象本身的元素(前提是有元素)。理解了這個函數本身的作用之后,我們看向這個函數的返回值,返回值類型使用了單獨的模板參數,這在前一個解引用運算符重載中講過了,這里也是如此,

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

這里的Ptr指的T*(或const T*)。我們再看向返回值本身,_pNode->_val取地址,這個函數返回之后會發生什么呢,假如我們寫了以下代碼

std::cout << it->a << std::endl;

it->使用運算符重載返回了一個指針,那代碼就變成了一個指針和一個元素中間沒有運算符,這因該是會報錯的操作,但這里是會正常編譯通過的,因為c++在這里又做了特殊處理給這兩個變量中間加上了一個->,所以時候事實上來說代碼應該是這樣的

std::cout << it->->a << std::endl;//演示一下,事實上會報錯

可以看出c++為了增加代碼可讀性還是做出了很多妥協的。

++、–運算符重載

Self& operator++()
{_pNode = _pNode->_pNext;return *this;
}Self operator++(int)
{Self tmp(*this);_pNode = _pNode->_pNext;return tmp;
}Self& operator--()
{_pNode = _pNode->_pPrev;return *this;
}Self& operator--(int)
{Self tmp(*this);_pNode = _pNode->_pPrev;return tmp;
}

之后還要說明的就是迭代器的++和–的運算符重載,因為之前我們也說過,list是內存不連續的容器,所以++和–運算符都不能直接以指針++和–的形式實現,而是使用ListNode中_pPrev和_pNext指針來實現迭代,至于前置++(–)和后置++(–)的書寫區別,之前的文章也講過,因為它們是單參運算符,無法通過位置識別從而進行不同的操作,所以c++特別規定++(–)運算符重載時在參數列表多加上int的是后置++(–),沒加的是前置++(–)。

list類

template<class T>
class list
{typedef ListNode<T> Node;typedef Node* PNode;
public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;
public:///// List的構造 list(){CreateHead();}list(int n, const T& value = T()){CreateHead();while(n--){push_back(value);}}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*(first++));}}list(const list<T>& l){CreateHead();for (auto& e : l){push_back(e);}}list<T>& operator=(list<T> l){swap(l);return *this;}~list(){clear();delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return _pHead->_pNext;}iterator end(){return _pHead;}const_iterator begin()const{return _pHead->_pNext;}const_iterator end()const{return _pHead;}///// List Capacitysize_t size()const{return _size;}bool empty()const{return _pHead == _pHead->_pNext;//size == 0;}// List AccessT& front(){return _pHead->_pNext->_val;}const T& front()const{return _pHead->_pNext->_val;}T& back(){return _pHead->_pPrev->_val;}const T& back()const{return _pHead->_pPrev->_val;}// List Modifyvoid push_back(const T& val){ insert(end(), val); }void pop_back() {erase(--end()); }void push_front(const T& val){insert(begin(), val); }void pop_front() {erase(begin()); }// 在pos位置前插入值為val的節點iterator insert(iterator pos, const T& val){Node* newnode = new Node(val);newnode->_pPrev = pos._pNode->_pPrev;newnode->_pNext = pos._pNode;pos._pNode->_pPrev->_pNext = newnode;pos._pNode->_pPrev = newnode;++_size;return newnode;}//iterator insert(iterator pos, const T& x)//{//    Node* cur = pos._pNode;//    Node* prev = cur->_pPrev;//    Node* newnode = new Node(x);//    prev->_pNext = newnode;//    newnode->_pNext = cur;//    cur->_pPrev = newnode;//    newnode->_pPrev = prev;//    //++_size;//    return newnode;//}// 刪除pos位置的節點,返回該節點的下一個位置iterator erase(iterator pos){pos._pNode->_pNext->_pPrev = pos._pNode->_pPrev;pos._pNode->_pPrev->_pNext = pos._pNode->_pNext;iterator ret = pos._pNode->_pNext;delete pos._pNode;--_size;return ret;}void clear(){iterator cur = begin();while (cur != end()){cur = erase(cur);}_size = 0;}void swap(list<T>& l){std::swap(_pHead, l._pHead);std::swap(_size, l._size);}private:void CreateHead(){_pHead = new Node;_pHead->_pNext = _pHead;_pHead->_pPrev = _pHead;}PNode _pHead;size_t _size = 0;
};

list類的實現就比較公式化了,值得一說的就幾個,下面一一講解。

CreateHead(頭節點創建)

void CreateHead()
{_pHead = new Node;_pHead->_pNext = _pHead;_pHead->_pPrev = _pHead;
}

首先是CreateHead(),這個函數被用于創建頭節點,我們實現的list的底層是帶頭雙向鏈表,頭節點是必須的,這個函數在很多成員函數中都會用到,所以單獨封裝并放進private訪問限定符中限制外部訪問。函數實現思路也很簡單,new一個節點出來,指針首尾相連就行。

insert

iterator insert(iterator pos, const T& val)
{Node* newnode = new Node(val);newnode->_pPrev = pos._pNode->_pPrev;newnode->_pNext = pos._pNode;pos._pNode->_pPrev->_pNext = newnode;pos._pNode->_pPrev = newnode;++_size;return newnode;
}

之后是insert函數,insert函數可以被反復復用到一些成員函數之中,十分方便。實現思路就是創建一個節點插入pos迭代器指向的節點的前面,接一下指針就行,之后返回新插入的結點防止迭代器失效。

erase

iterator erase(iterator pos)
{pos._pNode->_pNext->_pPrev = pos._pNode->_pPrev;pos._pNode->_pPrev->_pNext = pos._pNode->_pNext;iterator ret = pos._pNode->_pNext;delete pos._pNode;--_size;return ret;
}

erase函數也可以被復用到一些成員函數之中,非常方便,實現思路就是將pos指向的結點的前一個和后一個相接之后刪除這個結點,之后返回原本pos指向的結點的下一個結點。

賦值運算符重載

list<T>& operator=(list<T> l)
{swap(l);return *this;
}

賦值運算符重載,這里我們故意不用引用,這樣傳過來的參數就是拷貝構造好的需要被賦值成的對象,直接交換,由于臨時變量的生命周期出了作用域就沒了,所以正好把之前的對象的銷毀,完美完成交換。

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

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

相關文章

Redis持久化:

什么是Redis持久化&#xff1a; Redis 持久化是指將 Redis 內存中的數據保存到硬盤等持久化存儲介質中&#xff0c;以便在 Redis 服務器重啟或出現故障時能夠恢復數據&#xff0c;保證數據的可靠性和持續性。Redis 提供了兩種主要的持久化方式&#xff1a;RDB&#xff08;Redi…

VBA 64位API聲明語句第009講

跟我學VBA&#xff0c;我這里專注VBA, 授人以漁。我98年開始&#xff0c;從源碼接觸VBA已經20余年了&#xff0c;隨著年齡的增長&#xff0c;越來越覺得有必要把這項技能傳遞給需要這項技術的職場人員。希望職場和數據打交道的朋友&#xff0c;都來學習VBA,利用VBA,起碼可以提高…

在pycharm profession 2020.3將.py程序使用pyinstaller打包成exe

一、安裝pyinstaller 在pycharm的項目的Terminal中運行pip3 install pyinstaller即可。 安裝后在Terminal中輸入pip3 list看一下是否成功 二、務必在在項目的Terminal中輸入命令打包&#xff0c;命令如下&#xff1a; python3 -m PyInstaller --noconsole --onefile xxx.py …

Unity SpriteRenderer(精靈渲染器)

&#x1f3c6; 個人愚見&#xff0c;沒事寫寫筆記 &#x1f3c6;《博客內容》&#xff1a;Unity3D開發內容 &#x1f3c6;&#x1f389;歡迎 &#x1f44d;點贊?評論?收藏 &#x1f50e;SpriteRenderer:精靈渲染器 &#x1f4a1;Sprite Renderer是精靈渲染器&#xff0c;所有…

2.LED燈的控制和按鍵檢測

目錄 STM32F103的GPIO口 GPIO口的作用 GPIO口的工作模式 input輸入檢測 -- 向內檢測 output控制輸出 -- 向外輸出 寄存器 寄存器地址的確定 配置GPIO口的工作模式 時鐘的開啟和關閉 軟件編程驅動 LED 燈 硬件 軟件 軟件編程驅動 KEY 按鍵 硬件 軟件 按鍵消抖 代碼 STM32F…

Flink 的狀態機制

在實時流處理領域&#xff0c;狀態管理是構建復雜業務邏輯的核心能力。Apache Flink 通過統一的狀態抽象和高效的容錯機制&#xff0c;為開發者提供了從毫秒級窗口聚合到 TB 級歷史數據關聯的全場景支持。本文將深入剖析 Flink 狀態機制的底層原理&#xff0c;結合實際案例展示…

【查看.ipynp 文件】

目錄 如何打開 .ipynb 文件&#xff1f; 如果確實是 .ipynp 文件&#xff1a; .ipynp 并不是常見的 Jupyter Notebook 文件格式。通常&#xff0c;Jupyter Notebook 文件的擴展名是 .ipynb&#xff08;即 Interactive Python Notebook&#xff09;。如果你遇到的是 .ipynb 文…

Runnable組件重試機制降低程序錯誤率

一、LangChain 重試機制深度解析 當構建生產級AI應用時&#xff0c;with_retry() 機制可有效提升系統容錯性&#xff0c;典型應用場景包括&#xff1a; API調用頻率限制時的自動恢復模型服務臨時不可用的故障轉移網絡波動導致的瞬時異常處理 參數詳解與配置策略 1. 參數配置…

k8s筆記——kubebuilder工作流程

kubebuilder工作流程 Kubebuilder 工作流程詳解 Kubebuilder 是 Kubernetes 官方推薦的 Operator 開發框架&#xff0c;用于構建基于 Custom Resource Definitions (CRD) 的控制器。以下是其核心工作流程的完整說明&#xff1a; 1. 初始化項目 # 創建項目目錄 mkdir my-opera…

Java框架“若依RuoYi”前后端分離部署

運行環境 Eclipse IDE for Enterprise Java and Web Developers 下載Eclipse解壓Eclipse到文件夾 Maven 下載Maven解壓Maven到文件夾配置環境變量MAVEN_HOME為Maven安裝位置配置環境變量path為%MAVEN_HOME%\bin Redis 下載Redis解壓Redis到文件夾配置環境變量path為Redis安裝位…

游戲引擎學習第249天:清理調試宏

歡迎大家&#xff0c;讓我們直接進入調試代碼的改進工作 接下來&#xff0c;我們來看一下上次停留的位置。如果我沒記錯的話&#xff0c;上一場直播的結尾我有提到一些我想做的事情&#xff0c;并且在代碼中留下了一個待辦事項。所以也許我們今天首先做的就是解決這個問題。但…

二極管反向恢復的定義和原理

二極管的反向恢復定義 二極管的反向恢復是指二極管從正向導通狀態切換到反向阻斷狀態時&#xff0c;電流從正向變為負向并最終回到零所需的時間。具體過程如下&#xff1a; 正向導通&#xff1a;當二極管正向偏置時&#xff0c;電流可以順利通過&#xff0c;此時二極管處于導…

音視頻開發技術總結報告

音視頻開發技術總結報告 一、音視頻開發基礎 1、音頻基礎 聲音原理 聲波特性&#xff1a;頻率、振幅、波長人耳聽覺范圍&#xff1a;20Hz-20kHz聲音三要素&#xff1a;音調、音量、音色 數字音頻基礎 采樣率&#xff1a;常見44.1kHz、48kHz、96kHz量化位數&#xff1a;8bit、…

中間件和組件

文章目錄 1. 前言2. 中間件介紹3. 組件介紹4. 區別對比5. 簡單類比6. 總結 中間件和組件 1. 前言 中間件和組件是軟件開發中兩個重要的概念&#xff0c;但它們的定位和作用完全不同。中間件解決的事通信、跨系統、安全等問題&#xff0c;組件是解決具體業務模塊&#xff0c;提高…

AI超級智能體教程(五)---自定義advisor擴展+結構化json輸出

文章目錄 1.自定義攔截器1.2自定義Advisor1.2打斷點調試過程1.3Re-reading Advisor自定義實現 2.戀愛報告開發--json結構化輸出2.1原理介紹2.1代碼實現2.3編寫測試用例2.4結構化輸出效果 1.自定義攔截器 1.2自定義Advisor spring里面的這個默認的是SimpleloggerAdvisor&#…

02_使用 AES 算法實現文件加密上傳至阿里云、解密下載

02_使用 AES 算法實現文件加密上傳至阿里云、解密下載 一、文件上傳下載接口 controller 層 RestController RequestMapping("/api/common/file") Api(tags "公共文件上傳") AllArgsConstructor Slf4j public class FileV2Controller {private final Os…

力扣:24兩兩交換鏈表的節點

目錄 1.題目描述&#xff1a; 2.算法思路&#xff1a; 3.代碼展示&#xff1a; 1.題目描述&#xff1a; 給你一個鏈表&#xff0c;兩兩交換其中相鄰的節點&#xff0c;并返回交換后鏈表的頭節點。你必須在不修改節點內部的值的情況下完成本題&#xff08;即&#xff0c;只能…

smss源代碼分析之smss!SmpLoadSubSystemsForMuSession函數分析加載csrss.exe

第一部分&#xff1a; Next SmpSubSystemsToLoad.Flink; while ( Next ! &SmpSubSystemsToLoad ) { p CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry )…

MIT6.S081-lab8前置

MIT6.S081-lab8前置 注&#xff1a;本部分除了文件系統還包含了調度的內容。 調度 調度涉及到保存寄存器&#xff0c;恢復寄存器&#xff0c;就這一點而言&#xff0c;和我們的 trap 很像&#xff0c;但是實際上&#xff0c;我們實現并不是復用了 trap 的邏輯&#xff0c;我…

哈希函數詳解(SHA-2系列、SHA-3系列、SM3國密)案例:構建簡單的區塊鏈——密碼學基礎

文章目錄 一、密碼哈希函數概述1.1 哈希函數的基本概念1.2 哈希函數在數據安全中的應用 二、SHA-2系列算法詳解2.1 SHA-2的起源與發展2.2 SHA-256技術細節與實現2.3 SHA-384和SHA-512的特點2.4 SHA-2系列算法的安全性評估 三、SHA-3系列算法詳解3.1 SHA-3的起源與設計理念3.2 K…