您的位置:首页 > 移动开发 > Objective-C

借鉴 C# 关于 LINQ 的设计思想用 C++ 11 来实现 LINQ to Object

2015-12-14 15:49 851 查看
  在 C# 里面 LINQ 是基于扩展方法来构建的,扩展的是 IEnumerable<T> 接口。有关扩展方法的好处在这里我就不做多的说明了,我默认看到此文章的读者都是喜欢 C# 并且理解 C# 这门语言的美妙的人~

  在 LINQ 的扩展方法里面,返回的依旧是个 IEnumerable<T> 接口的对象,于是 LINQ 拥有了链式调用的风格。如:

List<int> list = new List<int>(){1,2,3,4,5,6,7,8,9};
var query = list
.Where(x => x % 2 ==0)
.Select(x => x * x)
.Take(3);

//


  在每一次调用中,实际上是将上一个迭代器对象重新包装(装饰)了一遍,详细请看这篇文章。这样的好处就是可以实现延迟执行(毕竟返回的对象是迭代器),当迭代的时候才真正开始运算。

基于这样的思想,我们可以用 C++ 来实现一个 LINQ:

  由于 C++ 没有扩展方法,我们需要先将 STL 容器转换为一个 linq_enumerable 对象,里面保存着 STL 的迭代器。而在每一次的 LINQ 函数调用中,都将当前迭代器对象包装(装饰)一次,并重新返回一个 linq_enumerable 对象。

  我们可以用 from 函数来实现转换:

template<typename TContainer>
auto from(const TContainer& c)->linq_enumerable<decltype(std::begin(c))>
{
return linq_enumerable<decltype(std::begin(c))>(std::begin(c), std::end(c));
}


  linq_enumerable 类如下:

template<typename TIterator>
class linq_enumerable
{
private:
TIterator _begin;
TIterator _end;

public:
linq_enumerable(const TIterator& b, const TIterator& e) :
_begin(b), _end(e)
{}

TIterator begin()const
{
return _begin;
}

TIterator end()const
{
return _end;
}
};


  然后我们来测试一下:

int main()
{
{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for (auto x : from(v))
{
cout << x << endl;
}
}

{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for (auto x : from(from(from(from(v)))))
{
cout << x << endl;
}
}
return 0;
}


  好了,到这里我们已经将所有的准备工作都做好了。接下来的一个个 LINQ 函数,都是在基于这之上一点点增加的。

那么,我们就先从最简单的 select 开始吧:

  首先,select() 是 linq_enumerable 对象的成员函数,它接收一个 lambda 函数 ,然后将当前 linq_enumerable 对象的迭代器对象包装为 select_iterator ,最后返回linq_enumerable 对象。

template<typename TFunction>
auto select(const TFunction& f)const->linq_enumerable<select_iterator<TIterator, TFunction>>
{
return linq_enumerable<select_iterator<TIterator, TFunction>>(
select_iterator<TIterator,TFunction>(_begin,f),
select_iterator<TIterator,TFunction>(_end,f)
);
}


  select_iterator 对象的成员应该要有 被包装的迭代器、lambda 函数对象,同时还要重载 ++ * == != 这几种操作符(自增、取值、等于、不等于)。select_iterator 类的实现如下:

template<typename TIterator,typename TFunction>
class select_iterator
{
typedef select_iterator<TIterator, TFunction> TSelf;

private:
TIterator iterator;
TFunction f;

public:
select_iterator(const TIterator& i, const TFunction& _f) :
iterator(i), f(_f)
{}

TSelf& operator++()
{
++iterator;
return *this;
}

auto operator*()const->decltype(f(*iterator))
{
return f(*iterator);
}

bool operator==(const TSelf& it)const
{
return it.iterator == iterator;
}

bool operator!=(const TSelf& it)const
{
return it.iterator != iterator;
}
};


  现在我们可以再来测试一下:

{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto q = from(v).select([](int x) { return x + 10; });

vector<int> xs = { 11, 12, 13, 14, 15, 16, 17, 18, 19 };

assert(std::equal(xs.begin(), xs.end(), q.begin()));
}

{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto q = from(v).select([](int x) { return x * x; });

vector<int> xs = { 1, 4, 9, 16, 25, 36, 49, 64, 81 };

assert(std::equal(xs.begin(), xs.end(), q.begin()));
}


  我们看到了预期的结果,此时变量 q 的类型是类似于 linq_enumerable<select_iterator<std::vector<int>::iterator>> 这样(忽略TFunction类型),LINQ 函数的执行过程实际上只是将 迭代器 对象包装了一次。

那么我们再来看看 where:

  where_iterator 对象和 select_iterator 对象很相似,稍微有点区别的地方在于 自增 和 取值 操作。当 where_iterator 自增时,它会有一个谓词条件,若没满足这个条件则会继续自增,以此过滤掉不满足条件的元素。where_iterator 的取值操作就很简单了,直接对它所包装的 迭代器对象进行 * 操作即可。实现如下:

template<typename TIterator,typename TFunction>
class where_iterator
{
typedef where_iterator<TIterator, TFunction> TSelf;

private:
TIterator iterator;
TIterator end;
TFunction f;

public:
where_iterator(const TIterator& i, const TIterator& e, const TFunction& _f) :
iterator(i), end(e), f(_f)
{
while (iterator != end && !f(*iterator))
{
++iterator;
}
}

TSelf& operator++()
{
if (iterator == end) return *this;
++iterator;
while (iterator != end && !f(*iterator))
{
++iterator;
}
return *this;
}

iterator_type<TIterator> operator*()const
{
return *iterator;
}

bool operator==(const TSelf& it)const
{
return it.iterator == iterator;
}

bool operator!=(const TSelf& it)const
{
return iterator != it.iterator;
}
};


  在取值操作中,返回值类型是 迭代器所指向的元素的类型,在这里我用 iterator_type 来实现。

template<typename TIterator>
using iterator_type = decltype(**(TIterator*)nullptr);


  nullptr 是 C++ 11 标准中用来表示空指针的常量值,可以将其强制转换为指向 TIterator 的指针,然后对其解引用得到一个不存在的 TIterator 对象 *(TIterator*)nullptr ,而再对 迭代器对象进行解引用,即可得到 迭代器所指向的元素。最后对其使用 decltype 操作,得到元素类型。对了,我们还要实现 linq_enumerable 对象的 where 函数:

template<typename TFunction>
auto where(const TFunction& f)const->linq_enumerable<where_iterator<TIterator,TFunction>>
{
return linq_enumerable<where_iterator<TIterator, TFunction>>(
where_iterator<TIterator, TFunction>(_begin,_end,f),
where_iterator<TIterator, TFunction>(_end,_end,f)
);
}


  where 也完成了,我们赶紧来测试一下:

{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto q = from(v).where([](int x) { return x % 2 == 1; });

vector<int> xs = { 1, 3, 5, 7, 9 };

assert(std::equal(xs.begin(), xs.end(), q.begin()));
}

{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto q = from(v).where([](int x) { return x > 5; });

vector<int> xs = { 6, 7, 8, 9 };

assert(std::equal(xs.begin(), xs.end(), q.begin()));
}

{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto q = from(v)
.where([](int x) { return x % 2 == 1; })
.select([](int x) { return x * 10; });

vector<int> xs = { 10, 30, 50, 70, 90 };

assert(std::equal(xs.begin(), xs.end(), q.begin()));
}

{
vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto q = from(v)
.where([](int x) { return x % 2 == 1; })
.where([](int x) { return x > 5; })
.select([](int x) { return x * 10; });

vector<int> xs = { 70, 90 };

assert(std::equal(xs.begin(), xs.end(), q.begin()));
}


  看到这样的结果,是不是觉得已经要大功告成了。简单来说,差不多是这样。LINQ 的各个函数之间是独立存在的,假如你只需要用到 LINQ 的 过滤 和 投影 的话,那么可以说我们已经完成了 LINQ to Object 的实现.....

在微软的官方文档中有 101个 LINQ 实例,要方便一点也可以看这里。这 101个例子几乎包括了所有的 LINQ 操作,我们可以据此将 LINQ 操作分为以下几类:

  1、Restriction Operators. 如:Where

  2、Projection Operators. 如:Select

  3、Partitioning Operator. 如:Take

  4、Ordering Operators. 如:OrderBy

  5、Grouping Operators. 如:GroupBy

  6、Set Operators. 如:Distinct

  7、Conversion Operators. 如:ToList

  8、Element Operators. 如:First

  9、Generation Operators. 如:Range

  10、Quantifiers. 如:Any

  11、Aggregate Operators. 如:Count

  12、Miscellaneous Operators. 如:Concat

  13、Join Operators. 如:Cross Join 和 Group Join

  在本文中,我会每一类给出一个实现,同一类别其他操作的实现细节大家可以看我的代码

  take 函数与 select 和 where 类似,也是先将迭代器包装成 take_iterator ,然后返回 linq_enumerable 对象。

auto take(int count)const->linq_enumerable<take_iterator<TIterator>>
{
return linq_enumerable<take_iterator<TIterator>>(
take_iterator<TIterator>(_begin,_end,count),
take_iterator<TIterator>(_end,_end,count)
);
}


  take_iterator 类实现如下:

template<typename TIterator2, typename TFunction1, typename TFunction2, typename TFunction3>
auto join(const linq_enumerable<TIterator2>& e,
const TFunction1& keySelector1,
const TFunction2& keySelector2,
const TFunction3& resultSelector)const
->linq<decltype(resultSelector(*(TElement*)nullptr, **(TIterator2*)nullptr))>
{
typedef decltype(resultSelector(*(TElement*)nullptr, **(TIterator2*)nullptr)) TResultValue;
auto result = std::make_shared<std::vector<TResultValue>>();

for (auto it1 = _begin; it1 != _end; ++it1)
{
auto value1 = *it1;
auto key1 = keySelector1(value1);
for (auto it2 = e.begin(); it2 != e.end(); ++it2)
{
auto value2 = *it2;
auto key2 = keySelector2(value2);
if (key1 != key2) continue;

result->push_back(resultSelector(value1, value2));
}
}
return from_values(result);
}


join
  最后的最后,就是测试代码了!

struct person
{
string name;
};

struct pet
{
string name;
person owner;
};

person fek = { "尔康, 福"};

person ylc = { "良辰, 叶" };
person hmj = { "美景, 花" };
person lks = { "看山, 刘" };
person lat = { "傲天, 龙" };
person persons[] = { ylc, hmj, lks, lat };

pet dog = { "斯派克", ylc };
pet cat = { "汤姆", ylc };
pet mouse = { "杰瑞", hmj };
pet bird = { "愤怒的小鸟", lks };
pet pig = { "风口上的猪", fek };
pet pets[] = { dog, cat, mouse, bird, pig };

auto person_name = [](const person& p) { return p.name; };
auto pet_owner_name = [](const pet& p) { return p.owner.name; };
auto result = [](const person& p, const pet& pp) { return std::make_tuple(p.name, pp.name); };

/*
良辰, 叶 : 斯派克
良辰, 叶 : 汤姆
美景, 花 : 杰瑞
看山, 刘 : 愤怒的小鸟
*/
for (auto x : from(persons).join(from(pets), person_name, pet_owner_name, result))
{
cout << get<0>(x) << " : " << get<1>(x) << endl;
}


  

  文章写到这里,也就差不多要结束了。写这文章最初的目的是学习并记录,而且最好的学习方式是说出来,爱因斯坦就说了,如果你不能简单说清楚,那就是还没有完全弄明白。所以......其实我也并非完全弄明白了,但我还是试图讲清楚一部分的东西吧!

  我是个两年多经验的野生菜鸟猿,如今在业余学习一下 C++,大家有想法可以提出来一起探讨一下,一起进步!

(完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: