• <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>

            huaxiazhihuo

             

            迭代器的抽象

                  迭代器是好東西,也是猿猴工具箱里面的七種武器之一。代碼中必然要操作一堆數(shù)據(jù),因此就要有容器,有了容器,自然就不可缺少迭代器,沒有迭代器,容器使用上就會非常不方便,并且還必須暴露其內(nèi)部實現(xiàn)方式。比如,在可憐的C語言里面,操作數(shù)組容器就要通過整數(shù)索引來訪問其元素,操作鏈表容器,就要通過while(node->next!=null)這樣的方式來訪問其元素。某種意義上講,整數(shù)索引,node->next這些都是迭代器,只是它們的使用方式?jīng)]有統(tǒng)一起來而已。既然如此,全世界的迭代器的使用方式都統(tǒng)一起來,那么這就是迭代器了。
                  基本上,現(xiàn)代化的語言,都會在語言層面上提供foreach之類的語法糖,其形式不外乎是,foreach(e:c){}。就是這樣,只要提供元素的名字和容器對象。后面跟著循環(huán)體。其思想就是從容器里面取出一個元素,用循環(huán)體對這個元素進行操作。循環(huán)完畢,就完成了對容器里面數(shù)據(jù)的操作。這種語法形式,簡潔得不能再簡潔了。很好很方便,什么額外重復的代碼都不勞費心了,甚至連類型推導不用做。真的,類型可以推導的時候,就讓編譯器推導好了。代碼里面必須大規(guī)模的使用auto,var這樣的關(guān)鍵字。不要擔心看不出來變量的類型。變量類型應(yīng)該從變量的名字中體現(xiàn)出來其抽象意義,當然,不要搞什么匈牙利命名法,那個太丑陋了。
                  既然語法糖提供了這種對迭代器的支持操作語法,自然而然,只要涉及到一堆數(shù)據(jù)這樣的概念,不必局限于具體的容器(數(shù)組,鏈表,哈希表),文件夾也是一堆數(shù)據(jù),Composition模式也是一堆數(shù)據(jù),數(shù)列,……,等等所有這些,全部都是概念上的一堆數(shù)據(jù),只要提供了迭代器,猿猴就可以很優(yōu)雅的用foreach這樣的語法糖來統(tǒng)一操作數(shù)據(jù),多么方便,多么的多態(tài)。不管這一堆數(shù)據(jù)的內(nèi)部實現(xiàn)方式是什么,后期怎么修改,在foreach這里代碼全部都不會受影響。更何況,對于迭代器,語法上不僅僅提供foreach的便利得無以復加的甜糖,還有一大堆的標準庫函數(shù)來讓猿猴操作迭代器,什么排序,查找,映射……。更令人發(fā)指的是,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 粉反對,說什么代碼不透明了,隱藏了代碼背后可能的復雜實現(xiàn)。對于這一簇人的堅持不懈反對抽象的態(tài)度,真不知該說什么好呢?代碼的能力里面,最最重要的事情就是抽 象,通過抽象,猿猴才可以避開細節(jié),將精力集中于更加重要更加復雜的事情。通過抽象,可以減少重復的代碼,可以提高類型安全。C++是唯一能在玩抽象概念的同時,又可以兼顧到底層細節(jié)的處理,從而不僅能寫出高效代碼,還能玩出更炫的技巧。很多時候,必須底層玩得越深,抽象的觸角才能伸得越高。
                  其實,迭代器不必依存于容器。而是,先有了迭代器,才會有容器。請謹記,迭代器可以獨立存在。begin和end就代表了一堆數(shù)據(jù)的概念。至于這一堆數(shù)據(jù)是如何存放的,這一切都無關(guān)緊要。基于此,有必要用class來表達一堆數(shù)據(jù)這么一個通用性極高的概念。其實,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概念上就支持一堆數(shù)據(jù)的操作,只要一個類型struct或者class里面有begin()和end()這一對活寶,并且這一對活寶的返回類型是迭代器,那么就可以盡情的享用foreach的甜糖。那么,何謂迭代器。就是支持三種操作的數(shù)據(jù)類型:!=(判斷相等,用來結(jié)束迭代操作),前++(用來到迭代到下一個元素),*(取值)。那么,這就是迭代器了,顯然,指針就是原生的迭代器。雖然,整形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;
            };
            然后,再用一個函數(shù)FromTo(也不知叫什么名字更好),用來生成DataRange。請注意,我們的迭代器怎么實現(xiàn),那都是細節(jié)。最后展示在用戶層代碼都是干干凈凈的function生成的DataRange,甚至連尖括號都不見了。也不用寫具體是什么類型的DataRange,只須用auto讓編譯器自動推導類型就好了。
            // step = 1是偷懶做法,萬一Step的構(gòu)造函數(shù)不能以1為參數(shù)就弱雞了。比如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));
            }
            于是,F(xiàn)romTo(1, 10, 2)就表示10以內(nèi)的所有奇數(shù),可以用for range的語法糖打印出來。
                  這里的FromTo是按照上升狀態(tài)產(chǎn)生一系列數(shù)據(jù),同樣,也可以產(chǎn)生下降的一堆數(shù)據(jù)FromDownTo,如果愿意的話,同學們也可以用迭代器形式生成斐波那契數(shù)列。不知注意到了,請用抽象的角度理解++和*這兩個操作符。++就是為新的數(shù)據(jù)做準備進入到下一個狀態(tài),根據(jù)情況,可以有不同方式,進入到下一個狀態(tài),比如上面的ValueIncrementIterator根據(jù)步長遞增到新的數(shù)值,ValueDecrementIterator的++卻是在做減法,甚至還可以做Filter操作;*就是取到數(shù)據(jù),我們可以在*的時候,才生成一個新的數(shù)據(jù),這里從某種意義上來講,其實就是延遲求值;而!=判斷結(jié)束條件的方式又多種多樣。總之,憑著這三個抽象操作,花樣百出,基本上已經(jīng)能夠覆蓋所有的需求了。
                  為了體現(xiàn)這種抽象的威力,讓我們給DataRange增加一個函數(shù)Concate,用于將兩堆數(shù)據(jù)串聯(lián)成一堆數(shù)據(jù)。首先,定義一個游走于兩堆數(shù)據(jù)的迭代器,當它走完第一堆數(shù)據(jù),就進入第二堆數(shù)據(jù)。
            //不知道有什么語法能推導迭代器的值類型,所以搞這個輔助函數(shù)。可能寫成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函數(shù)就很好辦了。
                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;
                }
                 這樣,就把兩堆數(shù)據(jù)串聯(lián)在一塊了,是不是很酷呢?用C++11寫代碼,很有行云流水的快感,又有函數(shù)式編程的風格。下期節(jié)目繼續(xù)發(fā)揮,給DataRange加入Filter,Map,Replace等操作,都是將一個DataRange變換成另一個DataRange的操作,顯然,這是一種組合子的設(shè)計方式,也是吸收了haskell和linq的設(shè)計思路。某種意義上講,就是給迭代器設(shè)計一套dsl,通過.操作符自由組合其成員函數(shù),達到用起來很爽的效果,目標就是僅僅通過幾個正交成員函數(shù)的隨意組合,可以在大多數(shù)情況下代替stl算法的鬼麻煩的寫法。這種dsl的最大好處類似于linq,先處理的步驟寫在最前面,避開了函數(shù)調(diào)用的層次毛病,最外層的函數(shù)反而寫在頂層。其實迭代器這個話題要展開來說的話,很有不少內(nèi)容,比如用stackless協(xié)程來偽裝成迭代器,F(xiàn)oldl,F(xiàn)oldl1,Scan等。當然,真要用得爽,還要配合boost中l(wèi)ambda的語法,好比什么_1+30,_1%2,當然,那個也可以自己寫,因為C++現(xiàn)在已經(jīng)支持lambda了,所以,自己寫boost lambda的時候,可以剪裁,取其精華,去其糟粕。如果,再弄一個支持arena內(nèi)存批量釋放又或者是Stack風格的allocator(線程相關(guān)),那么就更不會有任何心智負擔了,內(nèi)存的分配和釋放飛快,這樣的動多態(tài)的allocator寫起來也很有意思,它可以根據(jù)不同情況表現(xiàn)不同行為,比如說多線程下,就會用到線程同步,單線程就無須同步,每個線程單獨擁有一個allocator,根據(jù)用戶需要,還能用棧式內(nèi)存分配,也就是分配內(nèi)存時只是修改指針而已,釋放時就什么都不做了,最后通過析構(gòu)函數(shù),將此allocator的內(nèi)存一次性釋放。當擁有一個表現(xiàn)如此多樣的allocator,stl用起來真是爽。

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

            評論

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

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

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

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

            導航

            統(tǒng)計

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            評論排行榜

            韩国三级中文字幕hd久久精品| 久久久久久久91精品免费观看| 色婷婷综合久久久久中文| 久久精品中文字幕大胸| 久久精品麻豆日日躁夜夜躁| 久久免费线看线看| 久久综合九色综合欧美就去吻| 久久精品成人欧美大片| 韩国无遮挡三级久久| 久久久久久无码国产精品中文字幕| 中文字幕精品无码久久久久久3D日动漫 | 欧美激情精品久久久久久| 狠狠色丁香久久婷婷综合_中| AV色综合久久天堂AV色综合在| 精品久久久久久99人妻| 伊人久久大香线蕉av不卡| 99久久精品费精品国产| 久久精品国产久精国产果冻传媒 | 亚洲AV日韩精品久久久久| 99国内精品久久久久久久| 99久久香蕉国产线看观香| 成人午夜精品久久久久久久小说| 久久国产劲爆AV内射—百度| 日本免费一区二区久久人人澡 | 国产成人精品久久一区二区三区| 香蕉99久久国产综合精品宅男自| 国产91色综合久久免费分享| 久久久久久久女国产乱让韩| 大香网伊人久久综合网2020| 国产69精品久久久久777| 精品国产乱码久久久久久呢| 国产精品久久精品| 久久精品人人做人人爽电影蜜月 | 日韩av无码久久精品免费| 国产69精品久久久久APP下载| 成人午夜精品久久久久久久小说| 亚洲国产精品久久久久婷婷软件| 久久精品国产精品青草app| 久久青青草原亚洲av无码app | 久久久久人妻一区精品| 精品乱码久久久久久夜夜嗨|