有了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的所有排列組合的列表;