[C++]一份Linq to object的C++实现

[C++]一份Linq to object的C++实现

几个月的构想+0.5小时的设计+4小时的linq.h编码+3小时的测试编码。

大量使用C++11的特性,在GCC 4.7.2下编译通过。

 

关于实现相关的描述就不说了,我表达能力差,恐怕讲清楚还需要好几个小时。具体使用参见测试码。

上代码:

(1) linq.h

View Code

(2) 测试代码main.cpp (比我的代码更烂的是我的英语)

复制代码
#include "pch.h" 

#include "linq.h"

#include <vector>
#include <string>
#include <algorithm>

template<typename T>
void printC(const T& v)
{
    for (auto i : v) cout << i << ',';
    cout << endl;
}

template<typename T>
void print(const T& v)
{
    cout << v << endl;
}

bool startsWith(const std::string& s, const std::string& prefix)
{
    return s.find(prefix) == 0;
}

void featureTest()
{
    // 1. standard
    {
        auto query = range(10)
            .where([](int i){ return i % 2; })
            .select([](int i){ return i + 1; });
        auto ref = {2, 4, 6, 8, 10};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
    }
    // 2. deferred range
    {
        assert(range(123LL, 1000000000000LL).first() == 123);
    }
    // 3. deferred action
    {
        int selectCnt = 0;

        auto query = range(1000)
            .where([](int i){ return i % 2; })
            .select([&selectCnt](int i)
                { 
                    ++selectCnt;
                    return i; 
                })
            .where([](int i) { return i % 4; })
            .head(2);
        auto query2 = query;

        for (auto i : query) {}
        assert(selectCnt == 4);

        for (auto i : query2) {}
        assert(selectCnt == 8);
    }
    // 4. copy semantic
    {
        auto query = range(10).head(5);
        auto query2 = query;

        auto ref = {0, 1, 2, 3, 4};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
        assert(std::equal(ref.begin(), ref.end(), query2.begin()));

        auto iter = query.begin();
        ++iter;
        auto iter2 = iter;

        ref = {1, 2, 3, 4};
        assert(std::equal(ref.begin(), ref.end(), iter));
        assert(std::equal(ref.begin(), ref.end(), iter2));
    }
    // 5. always reference the neweast data of dataSource
    {
        std::vector<std::string> dataSrc{"A_abc", "A_def", "B_abc", "B_def"};

        auto query = from(dataSrc)
            .head(3)
            .where([](const std::string& s) { return startsWith(s, "B_"); });

        auto ref = {"B_abc"};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));

        dataSrc.clear();
        dataSrc.shrink_to_fit();
        dataSrc = {"B#_abc", "B_123", "B_1234", "B_321", "B_111"};
        ref = {"B_123", "B_1234"};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
    }
    // 6. invoke the operator new as little as possible
    {
    }
    // 7. you can use query after the dataSource has been destroyed, by the use of 'reserve'
    {
        Enumerable<int> query;
        {
            std::vector<int> v{1, 2, 3, 4};
            // query = from(v).select([](int i){ return i % 2; });
            query = from(v).reserve().select([](int i){ return i % 2; });

            auto ref = {1, 0, 1, 0};
            assert(std::equal(ref.begin(), ref.end(), query.begin()));
        }

        auto ref = {1, 0, 1, 0};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
    }
    // 8. add action to an exist query
    {
        auto query = range(10).where([](int i){ return i < 5;});
        auto ref = {0, 1, 2, 3, 4};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));

        auto query2 = query.select([](int i){ return i * i; });
        ref = {0, 1, 4, 9, 16};
        assert(std::equal(ref.begin(), ref.end(), query2.begin()));
    }
}

void functionTest()
{
    // 1. from, select, where, cast
    {
        int a[]{5, 6, 7, 8, 9};
        auto query = from(a)
            .where([](int i){ return i % 3; })
            .select([](int i) { return i * i;});
        auto ref = {25, 49, 64};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
    }
    // 2. range, all, any
    {
        assert(range(10).all([](int i){ return i >= 0;}));
        assert(!range(10).all([](int i){ return i > 0;}));
        assert(from(std::vector<std::string>{"_a", "b"})
                .any([](const std::string& s){ return startsWith(s, "_"); }));
        assert(!from(std::vector<std::string>{"@a", "b"})
                .any([](const std::string& s){ return startsWith(s, "_"); }));
    }
    // 3. cast, average
    {
        assert(range(1, 5).average() == 2);
        assert(range(1, 5).cast<float>().average() == 2.5);
    }
    // 4. contain, count
    {
        int a[]{1, 2, 1, 1, 3, 2, };
        assert(from(a).contain(3));
        assert(!from(a).contain(4));
        assert(from(a).count(1) == 3);
        assert(from(a).count([](int i) { return i % 2; }) == 4);
    }
    // 5. first, last, head, tail
    {
        int a[]{3, 5, 7, 9, 11};
        assert(from(a).first() == 3);
        assert(from(a).last() == 11);

        auto ref = {3, 5};
        auto query = from(a).head(2);
        assert(std::equal(ref.begin(), ref.end(), query.begin()));

        ref = {7, 9, 11};
        query = from(a).tail(3);
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
    }
    // 6. groupBy
    {
        auto query = range(10).groupBy([](int i) { return i % 3;});
        int refs[][4] = {
            {0, 3, 6, 9},
            {1, 4, 7,},
            {2, 5, 8,},
        };
        int n = 0;
        for (auto i : query) {
            assert(i.first == refs[n][0]);
            assert(std::equal(i.second.begin(), i.second.end(), refs[n]));
            ++n;
        }
        assert(n == 3);
    }
    // 7. takeUntil, skipUntil
    {
        auto query = range(10).takeUntil([](int i){ return i > 5; });
        auto ref = {0, 1, 2, 3, 4, 5};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));

        query = range(10).skipUntil([](int i){ return i > 5; });
        ref = { 6, 7, 8, 9};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
    }
    // 8. max, min
    {
        int a[]{3, 2, 5, 8, 10, -3};
        assert(from(a).min() == -3);
        assert(from(a).max() == 10);
    }
    // 9. reduce
    {
        assert(range(1, 11).reduce([](int a, int b){ return a + b; }) == 55);
        assert(range(1, 11).reduce([](int a, int b){ return a * b; }, 0) == 0);
        assert(range(1, 11).reduce([](int a, int b){ return a * b; }, 1) == 3628800);
    }
    // 10. unique, sort, random
    {
        int a[]{3, 5, 5, 4, 2, 1, 2};

        auto query = from(a).unique();
        auto ref = {3, 5, 4, 2, 1};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));

        ref = {5, 4, 3, 2, 1};
        query = query.sort().sort([](int a, int b){ return a > b; });
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
        query = query.random();
        assert(!std::equal(ref.begin(), ref.end(), query.begin()));
    }
    // 11. intersect, _union
    {
        int a[]{3, 5, 11};
        auto query = range(10).intersect(from(a));
        auto ref = {3, 5};
        assert(std::equal(ref.begin(), ref.end(), query.begin()));

        ref = {3, 4, 5, 6};
        query = query._union(range(4, 7));
        assert(std::equal(ref.begin(), ref.end(), query.begin()));
    }
}

int main()
{
    featureTest();
    functionTest();
}
复制代码

 

为什么不把它提交到git hub之类的专门代码仓库?一则我没有用过,二则,这种代码是我的write-only构想实践码,不提供后续维护的:)

#1楼 [ 楼主] 2012-10-21 21:20 | Scan.  
实现linq的延迟特性的最佳设施是c#的yield return,我老早就考虑用lambda表达式作为yield的替代方案。
最后终于在python和c++中各实现了一次linq。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值