C++11 模板元編程 - TypeList基本算法


有了list的結構定義,我們就可以為其定義相關算法了。由于list是遞歸結構,所以其算法也都是遞歸算法。

一般情況下遞歸算法的設計和數學歸納法比較類似,基本套路是先定義出算法中最顯而易見的值的結果(也就是遞歸結束條件),然后假設算法對“n - 1”已經可計算,再用其描述出對于“n”的算法。

對于用慣了使用命令式語言中循環語句(如C語言中while、for)的程序員,剛開始接觸和設計遞歸算法往往不是那么得心應手,但是相信通過刻意練習絕對是可以掌握這種思維方法的。

下面,我們首先實現求list長度的元函數Length

// "tlp/list/algo/Length.h"

template<typename TL> struct Length;

template<>
struct Length<NullType>
{
    using Result = IntType<0>;
};

template<typename Head, typename Tail>
struct Length<TypeElem<Head, Tail>>
{
    using Result = typename Add<IntType<1>, typename Length<Tail>::Result>::Result;
};

#define __length(...)   typename Length<__VA_ARGS__>::Result

Length,我們首先定義出空list的值為0。對于非空list,我們假設Length<Tail>已經計算出來了,那么整個list的長度就是Length<Tail>的結果再加一。

測試如下:

TEST("get the length of type list")
{
    ASSERT_EQ(__length(__type_list(int, char, short)), __int(3));
};

接下來我們來實現元函數TypeAt,給它一個list和一個index,它將返回對應list中index位置的元素。如果index非法,或者list為空,則返回__null()

// "tlp/list/algo/TypeAt.h"

template<typename TL, typename Index> struct TypeAt;

template<int V>
struct TypeAt<NullType, IntType<V>>
{
    using Result = NullType;
};

template<typename H, typename T>
struct TypeAt<TypeElem<H, T>, IntType<0>>
{
    using Result = H;
};

template<typename H, typename T, int i>
struct TypeAt<TypeElem<H, T>, IntType<i>>
{
    using Result = typename TypeAt<T , IntType<i - 1>>::Result;
};

#define __at(...) typename TypeAt<__VA_ARGS__>::Result

如上,先定義出對于空list,返回NullType;然后定義當index是0則返回當前list頭元素。最后定義當list非空,且index不為0的情況,就是返回剩余元素list中的第i - 1個元素。

針對它的測試如下:

TEST("get null from list by overflow index")
{
    using List = __type_list(int, char, short, long);

    ASSERT_INVALID(__at(List, __int(4)));
};

TEST("get the type by index")
{
    using List = __type_list(int, char, short, long);

    ASSERT_EQ(__at(List, __int(2)), short);
};

借助__index_of()我們可以實現出判斷某一元素是否在list中的元函數__is_included()

#define __is_included(...) __valid(__index_of(__VA_ARGS__))
TEST("estimate a type whether included in a list")
{
    using List = __type_list(int, short, long);

    ASSERT_TRUE(__is_included(List, int));
    ASSERT_FALSE(__is_included(List, char));
};

掌握了遞歸算法的設計技巧,類似地可以輕松實現__append()元函數,它的入參為list和類型T;它返回一個在入參list尾部追加類型T之后的新list;

// "tlp/list/algo/Append.h"

template<typename TL, typename T> struct Append;

template<>
struct Append<NullType, NullType>
{
    using Result = NullType;
};

template<typename T>
struct Append<NullType, T>
{
    using Result = typename TypeList<T>::Result;
};

template<typename H, typename T>
struct Append<NullType, TypeElem<H, T>>
{
    using Result = TypeElem<H, T>;
};

template<typename Head, typename Tail, typename T>
struct Append<TypeElem<Head, Tail>, T>
{
    using Result = TypeElem<Head, typename Append<Tail, T>::Result>;
};

#define __append(...) typename Append<__VA_ARGS__>::Result

針對__append()元函數的測試如下:

TEST("append a type to an empty list")
{
    ASSERT_EQ(__append(__empty_list(), char), __type_list(char));
};

TEST("append a list to an empty list")
{
    using List = __type_list(int, char);

    ASSERT_EQ(__append(__empty_list(), List), List);
};

TEST("append an empty list_to a list")
{
    using List = __type_list(int, char);

    ASSERT_EQ(__append(List, __empty_list()), List);
};

TEST("append a type to a list")
{
    using List = __type_list(int, short);
    using Expected = __type_list(int, short, long);

    ASSERT_EQ(__append(List, long), Expected);
};

TEST("append a list to a list")
{
    using List = __type_list(int, short);
    using Expected = __type_list(int, short, char, long);

    ASSERT_EQ(__append(List, __type_list(char, long)), Expected);
};

上面測試用例中出現的__empty_list()的定義如下:

// "tlp/list/EmptyList.h"

using EmptyList = NullType;

#define __empty_list()  EmptyList

關于基本算法的實現就介紹到這里。TLP庫中一共實現了關于list的如下基本元函數:

  • __length():入參為list,返回list的長度;

  • __at():入參為list和index,返回list中第index個位置的元素;

  • __index_of():入參為list和類型T,返回list中出現的第一個T的index位置;如果不存在則返回__null();

  • __is_included():入參為list和類型T,判斷T是否在list中;返回對應的BoolType;

  • __append():入參為list和類型T,返回一個新的list。新的list為入參list尾部追加類型T之后的list;

  • __erase():入參為list和類型T,返回一個新的list。新的list為入參list刪除第一個出現的類型T之后的list;

  • __erase_all():入參為list和類型T,返回一個新的list。新的list為入參list刪除所有出現的類型T之后的list;

  • __unique():入參為一個list,返回一個去除所有重復元素后的新的list。

  • __replace():入參為list和兩個類型T,U;返回一個將list中出現的第一個T替換成U之后的新list;

  • __replace_all():入參為list和兩個類型T,U;返回一個將list中出現的所有T替換成U之后的新list;

  • __is_subset():入參為兩個list,判斷前一個list是否為后一個的子集,返回BoolType;

  • __belong():入參為一個list和一個list的集合Lists,判斷list是否為Lists集合中任一元素的子集,返回BoolType;

  • __comb():入參為list和一個__int(N),返回由list對于N的所有排列組合的列表;


TypeList高階算法

返回 C++11模板元編程 - 目錄

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容