青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

huaxiazhihuo

 

迭代器的抽象

      迭代器是好東西,也是猿猴工具箱里面的七種武器之一。代碼中必然要操作一堆數據,因此就要有容器,有了容器,自然就不可缺少迭代器,沒有迭代器,容器使用上就會非常不方便,并且還必須暴露其內部實現方式。比如,在可憐的C語言里面,操作數組容器就要通過整數索引來訪問其元素,操作鏈表容器,就要通過while(node->next!=null)這樣的方式來訪問其元素。某種意義上講,整數索引,node->next這些都是迭代器,只是它們的使用方式沒有統一起來而已。既然如此,全世界的迭代器的使用方式都統一起來,那么這就是迭代器了。
      基本上,現代化的語言,都會在語言層面上提供foreach之類的語法糖,其形式不外乎是,foreach(e:c){}。就是這樣,只要提供元素的名字和容器對象。后面跟著循環體。其思想就是從容器里面取出一個元素,用循環體對這個元素進行操作。循環完畢,就完成了對容器里面數據的操作。這種語法形式,簡潔得不能再簡潔了。很好很方便,什么額外重復的代碼都不勞費心了,甚至連類型推導不用做。真的,類型可以推導的時候,就讓編譯器推導好了。代碼里面必須大規模的使用auto,var這樣的關鍵字。不要擔心看不出來變量的類型。變量類型應該從變量的名字中體現出來其抽象意義,當然,不要搞什么匈牙利命名法,那個太丑陋了。
      既然語法糖提供了這種對迭代器的支持操作語法,自然而然,只要涉及到一堆數據這樣的概念,不必局限于具體的容器(數組,鏈表,哈希表),文件夾也是一堆數據,Composition模式也是一堆數據,數列,……,等等所有這些,全部都是概念上的一堆數據,只要提供了迭代器,猿猴就可以很優雅的用foreach這樣的語法糖來統一操作數據,多么方便,多么的多態。不管這一堆數據的內部實現方式是什么,后期怎么修改,在foreach這里代碼全部都不會受影響。更何況,對于迭代器,語法上不僅僅提供foreach的便利得無以復加的甜糖,還有一大堆的標準庫函數來讓猿猴操作迭代器,什么排序,查找,映射……。更令人發指的是,C#把迭代器搗鼓得好用得讓人傷心難過悲憤欲絕,而linq語法上還可以把IEnumbale整成monad,可以用來作什么cps的變換。迭代器在手,天下我有。
      迭代器這個概念的抽象似乎很理所當然,但是不然,比如,劉未鵬舉過Extended STL的例子,操作文件夾。C和C++代碼對比。
// in C
DIR*  dir = opendir(".");
if(NULL != dir)
{
  
struct dirent*  de;
  
for(; NULL != (de = readdir(dir)); )
  {
    
struct stat st;
    
if0 == stat(de->d_name, &st) &&
        S_IFREG 
== (st.st_mode & S_IFMT))
    {
      remove(de
->d_name);
    }
  }
  closedir(dir);
}
 
// in C++
readdir_sequence entries(".", readdir_sequence::files); 
std::for_each(entries.begin(), entries.end(), ::remove);
顯然,前者是沒有迭代器的抽象,后者是有迭代器抽象的簡潔異常的代碼。第一次看到,驚為天人,其實本就該如此,只是C將這一切搞復雜了。當然,還有一批C 粉反對,說什么代碼不透明了,隱藏了代碼背后可能的復雜實現。對于這一簇人的堅持不懈反對抽象的態度,真不知該說什么好呢?代碼的能力里面,最最重要的事情就是抽 象,通過抽象,猿猴才可以避開細節,將精力集中于更加重要更加復雜的事情。通過抽象,可以減少重復的代碼,可以提高類型安全。C++是唯一能在玩抽象概念的同時,又可以兼顧到底層細節的處理,從而不僅能寫出高效代碼,還能玩出更炫的技巧。很多時候,必須底層玩得越深,抽象的觸角才能伸得越高。
      其實,迭代器不必依存于容器。而是,先有了迭代器,才會有容器。請謹記,迭代器可以獨立存在。begin和end就代表了一堆數據的概念。至于這一堆數據是如何存放的,這一切都無關緊要。基于此,有必要用class來表達一堆數據這么一個通用性極高的概念。其實,boost里面好像也有這么一個東西。就叫做DataRange吧。為何不叫Range,因為Range另有更重要用途,這么好的名字就是用來生成DataRange,代碼不會直接看到DataRange,都是通過Range來生成DataRange。
template<typename Iterator>
struct DataRange
{
    typedef Iterator IteratorType;

    DataRange(IteratorType beginIter, IteratorType endIter) : mBegin(beginIter), mEnd(endIter)
    {
    }

    IteratorType begin()
const { return mBegin; }
    IteratorType end()
const { return mEnd; }
    IteratorType mBegin;
    IteratorType mEnd;
};
      然后,隨便搞兩行代碼試試
   vector<int> vvv = { 1, 2, 3 };
    for (auto i : Range(vvv))
    {
        cout << i << endl;
    }
      其實,C++11概念上就支持一堆數據的操作,只要一個類型struct或者class里面有begin()和end()這一對活寶,并且這一對活寶的返回類型是迭代器,那么就可以盡情的享用foreach的甜糖。那么,何謂迭代器。就是支持三種操作的數據類型:!=(判斷相等,用來結束迭代操作),前++(用來到迭代到下一個元素),*(取值)。那么,這就是迭代器了,顯然,指針就是原生的迭代器。雖然,整形int也可以++,!=,但是不支持取值操作,所以int不是迭代器。下面就要把int變成迭代器。
template<typename Ty, typename Step>
struct ValueIncrementIterator
{
    typedef ValueIncrementIterator ThisType;
    typedef Ty ValueType;
    typedef Step StepType;

    ThisType(ValueType val, StepType step)
        :mValue(val), mStep(step){}

    
bool operator != (const ThisType& other) const
    {
        
return mValue < other.mValue;
    }

    ValueType 
operator* () const
    {
        
return mValue;
    }

    ThisType
& operator++ ()
    {
        mValue 
+= mStep;
        
return *this;
    }

    ValueType mValue;
    StepType mStep;
};
然后,再用一個函數FromTo(也不知叫什么名字更好),用來生成DataRange。請注意,我們的迭代器怎么實現,那都是細節。最后展示在用戶層代碼都是干干凈凈的function生成的DataRange,甚至連尖括號都不見了。也不用寫具體是什么類型的DataRange,只須用auto讓編譯器自動推導類型就好了。
// step = 1是偷懶做法,萬一Step的構造函數不能以1為參數就弱雞了。比如DateTime和TimeSpan
template<typename Ty, typename Step>
auto FromTo(Ty from, Ty to, Step step 
= 1-> DataRange<ValueIncrementIterator<Ty, Step>>
{
    typedef ValueIncrementIterator
<Ty, Step> ValueType;
    
return DataRange<ValueType>(ValueType(from, step), ValueType(to, step));
}
于是,FromTo(1, 10, 2)就表示10以內的所有奇數,可以用for range的語法糖打印出來。
      這里的FromTo是按照上升狀態產生一系列數據,同樣,也可以產生下降的一堆數據FromDownTo,如果愿意的話,同學們也可以用迭代器形式生成斐波那契數列。不知注意到了,請用抽象的角度理解++和*這兩個操作符。++就是為新的數據做準備進入到下一個狀態,根據情況,可以有不同方式,進入到下一個狀態,比如上面的ValueIncrementIterator根據步長遞增到新的數值,ValueDecrementIterator的++卻是在做減法,甚至還可以做Filter操作;*就是取到數據,我們可以在*的時候,才生成一個新的數據,這里從某種意義上來講,其實就是延遲求值;而!=判斷結束條件的方式又多種多樣。總之,憑著這三個抽象操作,花樣百出,基本上已經能夠覆蓋所有的需求了。
      為了體現這種抽象的威力,讓我們給DataRange增加一個函數Concate,用于將兩堆數據串聯成一堆數據。首先,定義一個游走于兩堆數據的迭代器,當它走完第一堆數據,就進入第二堆數據。
//不知道有什么語法能推導迭代器的值類型,所以搞這個輔助函數。可能寫成type_trait形式更好,就算偷懶吧
template<typename Iter>
auto GetIteratorValueType(Iter
* ptr) -> decltype(**ptr)
{
    
return **ptr;
}

template
<typename Iter1, typename Iter2>
struct ConcateIterator
{
    typedef ConcateIterator ThisType;
    typedef Iter1 Iter1Type;
    typedef Iter2 Iter2Type;
    
//typedef decltype(*mBegin1) ValueType;
    typedef decltype(GetIteratorValueType((Iter1Type*)nullptr)) ValueType;

    ThisType(Iter1Type begin1, Iter1Type end1, Iter2Type begin2)
        :mBegin1(begin1), mEnd1(end1), mBegin2(begin2), mInBegin2(
false){}

    ThisType(Iter1Type end1, Iter2Type begin2)    
//這里有些蹊蹺,不過也沒什么
        :mBegin1(end1), mEnd1(end1), mBegin2(begin2), mInBegin2(true){}

    
bool operator != (const ThisType& other) const
    {
        
if (!mInBegin2 && other.mInBegin2)
            
return true;
        
if (!mInBegin2 && !other.mInBegin2 && mBegin1 != other.mBegin1)
            
return true;
        
if (mInBegin2 && other.mInBegin2 && mBegin2 != other.mBegin2)
            
return true;
        
return false;
    }

    ValueType 
operator* () const
    {
        
return mInBegin2 ? (*mBegin2) : (*mBegin1);
    }

    ThisType
& operator++ ()
    {
        
if (mInBegin2)
        {
            
++mBegin2;
        }
        
else
        {
            
if (mBegin1 != mEnd1)
                
++mBegin1;
            
if (!(mBegin1 != mEnd1))
                mInBegin2 
= true;
        }
        
return *this;
    }

    Iter1Type mBegin1;
    Iter2Type mBegin2;
    Iter1Type mEnd1;
    
bool mInBegin2;
};

有了ConcateIterator,DataRange的Concate函數就很好辦了。
    template<typename OtherRange>
    auto Concate(
const OtherRange& otherRange)
        
->DataRange<ConcateIterator<IteratorType, decltype(otherRange.begin())>>
    {
        typedef ConcateIterator 
< IteratorType, decltype(otherRange.begin())> ResultIter;
        
return DataRange<ResultIter>(
            ResultIter(mBegin, mEnd, otherRange.begin()), ResultIter(mEnd, otherRange.end()));
    }
然后,試試
    list<int> numList = { 10, 11, 12 };
    for (auto i : Range(vvv).Concate(FromTo(4, 10, 2)).Concate(numList))   //后面隨便接容器
    {
        cout << i << endl;
    }
     這樣,就把兩堆數據串聯在一塊了,是不是很酷呢?用C++11寫代碼,很有行云流水的快感,又有函數式編程的風格。下期節目繼續發揮,給DataRange加入Filter,Map,Replace等操作,都是將一個DataRange變換成另一個DataRange的操作,顯然,這是一種組合子的設計方式,也是吸收了haskell和linq的設計思路。某種意義上講,就是給迭代器設計一套dsl,通過.操作符自由組合其成員函數,達到用起來很爽的效果,目標就是僅僅通過幾個正交成員函數的隨意組合,可以在大多數情況下代替stl算法的鬼麻煩的寫法。這種dsl的最大好處類似于linq,先處理的步驟寫在最前面,避開了函數調用的層次毛病,最外層的函數反而寫在頂層。其實迭代器這個話題要展開來說的話,很有不少內容,比如用stackless協程來偽裝成迭代器,Foldl,Foldl1,Scan等。當然,真要用得爽,還要配合boost中lambda的語法,好比什么_1+30,_1%2,當然,那個也可以自己寫,因為C++現在已經支持lambda了,所以,自己寫boost lambda的時候,可以剪裁,取其精華,去其糟粕。如果,再弄一個支持arena內存批量釋放又或者是Stack風格的allocator(線程相關),那么就更不會有任何心智負擔了,內存的分配和釋放飛快,這樣的動多態的allocator寫起來也很有意思,它可以根據不同情況表現不同行為,比如說多線程下,就會用到線程同步,單線程就無須同步,每個線程單獨擁有一個allocator,根據用戶需要,還能用棧式內存分配,也就是分配內存時只是修改指針而已,釋放時就什么都不做了,最后通過析構函數,將此allocator的內存一次性釋放。當擁有一個表現如此多樣的allocator,stl用起來真是爽。

posted on 2016-05-14 02:10 華夏之火 閱讀(1329) 評論(2)  編輯 收藏 引用 所屬分類: c++技術探討

評論

# re: 迭代器的抽象 2016-05-25 10:38 linda

迭代器的確是個好東西  回復  更多評論   

# re: 迭代器的抽象 2016-05-25 11:21 華夏之火

@linda
迭代器不僅僅是好東西,而是必須物,不可或缺。沒有迭代器的抽象,要么就是延遲求值,要么代碼就會寫起來很痛苦  回復  更多評論   

導航

統計

常用鏈接

留言簿(6)

隨筆分類

隨筆檔案

搜索

積分與排名

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产一区二区三区免费不卡| 欧美电影美腿模特1979在线看| 午夜久久资源| 一本色道久久综合亚洲精品不卡 | 久久精品免费播放| 亚洲国产精品va在线看黑人| 牛牛国产精品| 欧美日韩国产一级| 午夜精品福利一区二区蜜股av| 亚洲欧美日韩精品久久亚洲区 | 久久综合久久综合久久综合| 久久精品国产69国产精品亚洲| 亚洲国产日韩一级| 99国产精品久久久久老师| 国产嫩草影院久久久久| 鲁大师影院一区二区三区| 欧美承认网站| 欧美一区二区三区视频在线观看| 久久精品国产精品| 亚洲精品久久久久久久久| 亚洲午夜精品福利| 伊人狠狠色j香婷婷综合| 99视频精品在线| 国内一区二区在线视频观看| 91久久精品国产91久久性色| 欧美体内she精视频| 久久综合久久综合久久| 欧美日韩专区在线| 麻豆精品视频在线观看| 国产精品va在线播放我和闺蜜| 久久夜色精品亚洲噜噜国产mv| 欧美日韩123| 蜜臀久久久99精品久久久久久| 欧美先锋影音| 亚洲国产精品激情在线观看| 国产深夜精品| 在线亚洲一区二区| 亚洲精品欧洲精品| 久久久7777| 欧美一区二区三区婷婷月色| 欧美精品在线一区二区三区| 麻豆乱码国产一区二区三区| 国产精品主播| a4yy欧美一区二区三区| 亚洲国产精品va在线看黑人动漫| 欧美一进一出视频| 亚洲主播在线| 欧美日韩伦理在线免费| 亚洲国产岛国毛片在线| 在线观看日韩国产| 久久国产视频网| 欧美一区二区三区四区视频| 国产精品欧美久久| 一区二区三区免费看| 日韩小视频在线观看专区| 欧美在线免费视频| 欧美日韩亚洲视频| 亚洲精品国产精品国自产在线| 今天的高清视频免费播放成人 | 亚洲高清激情| 亚洲人妖在线| 欧美精品xxxxbbbb| 999亚洲国产精| 亚洲图片在线| 国产精品av免费在线观看| 日韩视频免费观看高清完整版| 亚洲美女91| 欧美日韩国产精品| 一区二区欧美精品| 欧美一区二区国产| 国产伊人精品| 久久精品国产v日韩v亚洲| 免费欧美视频| 亚洲精品一区在线| 欧美日韩精品一区二区天天拍小说| 最新成人在线| 亚洲在线免费| 国产午夜精品福利| 久久亚洲图片| 亚洲美女在线视频| 欧美一区二区三区在线观看视频 | 亚洲国产黄色| 欧美激情视频在线播放| 一本色道久久综合亚洲精品婷婷| 亚洲女性裸体视频| 国语对白精品一区二区| 免费在线观看日韩欧美| 99热免费精品在线观看| 欧美亚洲免费在线| 亚洲国产精品精华液2区45| 欧美激情视频一区二区三区免费| 在线亚洲+欧美+日本专区| 久久久天天操| 在线视频一区观看| 国产日韩在线亚洲字幕中文| 免费看成人av| 性欧美8khd高清极品| 亚洲国产欧美一区| 欧美一区二区免费视频| 亚洲精美视频| 国产欧美二区| 欧美精品日韩精品| 欧美在线综合视频| 亚洲毛片在线| 免费成人av在线| 亚洲欧美不卡| 亚洲精品资源| 精品1区2区3区4区| 国产精品久久久久久久久免费| 久久久久一区二区三区| 亚洲一区二区三区成人在线视频精品| 美女精品国产| 欧美一级一区| 亚洲天堂免费在线观看视频| 最新国产の精品合集bt伙计| 国产午夜精品久久久久久免费视| 欧美久久婷婷综合色| 久久婷婷国产综合精品青草| 亚洲欧美中文日韩v在线观看| 亚洲日本一区二区三区| 欧美成人精品h版在线观看| 欧美在线啊v| 亚洲一线二线三线久久久| 亚洲美女中出| 伊人激情综合| 国产一区三区三区| 国产精品私拍pans大尺度在线| 欧美日韩三级一区二区| 欧美精品一区二区久久婷婷| 久久综合中文字幕| 久久免费少妇高潮久久精品99| 亚洲欧美中文另类| 亚洲免费在线观看视频| 一区二区三区 在线观看视频| 91久久精品国产91性色tv| 欧美成人自拍| 亚洲成人在线视频播放| 久久精品99国产精品酒店日本| 午夜精品福利视频| 欧美伊人影院| 欧美伊人久久大香线蕉综合69| 欧美亚洲自偷自偷| 欧美一区二区视频在线观看| 欧美一级电影久久| 久久国产手机看片| 久久精品国产一区二区三区| 久久久精品性| 另类av导航| 欧美激情亚洲激情| 亚洲三级色网| 日韩系列在线| 亚洲一区在线免费| 久久黄色网页| 卡通动漫国产精品| 欧美伦理a级免费电影| 欧美日韩亚洲一区在线观看| 国产精品高潮呻吟| 国产一区二区看久久| 在线不卡中文字幕| 日韩一区二区高清| 午夜精品亚洲一区二区三区嫩草| 欧美在线观看视频在线| 狂野欧美激情性xxxx欧美| 亚洲福利免费| 亚洲视频中文字幕| 久久国产精品99精品国产| 久久婷婷国产综合精品青草| 欧美日本亚洲视频| 国产情人节一区| 亚洲高清av| 午夜精品短视频| 欧美二区不卡| 中文一区字幕| 久久青草久久| 国产精品成人aaaaa网站| 国内精品久久久久影院薰衣草 | 欧美午夜精品久久久久久人妖| 国产欧美日韩一级| 亚洲高清视频中文字幕| 亚洲免费视频在线观看| 欧美不卡激情三级在线观看| 夜久久久久久| 久久永久免费| 国产美女精品一区二区三区| 亚洲欧洲一区二区天堂久久| 亚洲免费在线播放| 欧美国内亚洲| 性欧美1819性猛交| 国产精品h在线观看| 亚洲国产精品123| 欧美一区二区三区免费视| 亚洲日韩欧美视频一区| 久久国产直播| 国产精品午夜国产小视频| 日韩天堂av| 欧美成人按摩| 欧美中文字幕在线播放| 国产精品腿扒开做爽爽爽挤奶网站| 亚洲国产一区二区三区青草影视 | 亚洲国产成人91精品|