2009-07-01 152 views
2

我正在C++中进行一些编码工作,而我所从事的许多工作都涉及分析数据集。很多时候我需要选择从STL容器的一些元素,很频繁,我写这样的代码:类似于SQL的语法选择命令式语言

using std::vector; 
vector<int> numbers; 
for (int i = -10; i <= 10; ++i) { 
    numbers.push_back(i); 
} 

vector<int> positive_numbers; 
for (vector<int>::const_iterator it = numbers.begin(), end = numbers.end(); 
     it != end; ++it 
) { 
    if (number > 0) { 
     positive_numbers.push_back(*it); 
    } 
} 

随着时间的推移for循环和它所包含的逻辑得到了很多更加复杂和不可读。这样的代码比SQL类似的SELECT语句不太令人满意,假设我有一个表叫号与名为“民”,而不是一个std向量列:: < INT>:

SELECT * INTO positive_numbers FROM numbers WHERE num > 0 

这是一个很大对我来说更具可读性,并且随着时间的推移还可以更好地扩展,我们代码库中的很多if语句逻辑变得复杂,依赖于顺序并且不可维护。如果我们可以在C++中执行类似SQL的语句而不必访问数据库,那么我认为代码的状态可能会更好。

有没有更简单的方法,我可以在C++中实现类似于SELECT语句的东西,我可以通过仅描述所需对象的特性来创建新的对象容器?我对C++还比较陌生,所以我希望能够通过模板元编程或巧妙的迭代器来解决这个问题。谢谢!

根据前两个答案编辑。谢谢,我不知道这是LINQ的实际情况。我主要在Linux和OSX系统上编程,并且对跨OSX,Linux和Windows的跨平台感兴趣。因此,这个问题更受教育的版本是 - 是否有跨平台的实现类似LINQ for C++?

回答

2

LINQ是.NET(或单上的非Windows平台上显而易见的答案,但在C++,它不应该是很难写类似的东西在自己的STL。

使用Boost.Iterator图书馆写有“选择”的迭代器,例如,其中一个跳过不满足给定谓词的所有元素。

加速已经有自己的文档中的一些相关的例子,我相信。 或者http://www.boost.org/doc/libs/1_39_0/libs/iterator/doc/filter_iterator.html实际上可能会做什么你需要开箱即用

无论如何,在C++中,你需要d基本上通过分层迭代器来实现相同的效果。

如果您有一个常规迭代器,它访问序列中的每个元素,则可以将其包装在过滤器迭代器中,该迭代器将基础迭代器递增,直到找到满足条件的值。然后你甚至可以在一个“选择”迭代器中将这个值转换为所需的格式。

这似乎是一个相当明显的想法,但我不知道它的任何完整实现。

+0

谢谢贾尔夫。我一直在寻找已经实现的神奇功能,但我可能会尝试使用迭代器和过滤器的建议。 – 2009-07-02 00:04:40

3

我想你已经描述了LINQ(C#和.NET 3.5功能)。你看过吗?

+0

这是一个.NET功能,所以它可以从我假设的C++中使用。 – 2009-07-01 22:55:08

4

您几乎完全描述了LINQ。这是一个.NET 3.5特性,所以你应该可以在C++中使用它。

4

你描述在支持的概念,如关闭,谓词仿函数功能的语言是常见的,功能性等

与上面的代码的问题是,它结合了:

  1. 用于遍历集合的逻辑(for循环)
  2. 要复制到另一个集合的元素必须满足的条件
  3. 将元素从一个集合复制到另一个集合的逻辑

实际上,(1)和(3)是样板文件,只要每次需要迭代将某些元素复制到另一个集合的集合时,它可能只是每次都会更改的条件代码。支持函数式编程的语言消除了这种样板。例如,在Groovy中你可以只用

def positive_numbers = numbers.findAll{it > 0} 

取代你的for循环上述尽管C++是不是有可能是其提供了函数式编程与STL集合支持库函数式语言。例如,Apache commons集合(也可能是Google的集合库)为使用Java集合的函数式编程提供了支持,即使Java本身不是一种功能性语言。

0

如果您想要在Linux/OS X上试用LINQ,请查看Mono。它是.NET Framework的一个端口,现在包含LINQ。

1

您正在使用STL容器。我会建议使用STL algorithms,这在很大程度上是直接出于集合论。 SQL选择被翻译为std::find_ifstd::lower_boundstd::upper_bound(在已排序的容器上)的重复应用程序。性能与循环大致相同,但语法更具说明性。

LINQ会给你类似的语法和操作,但除非用于超过IQueryable(即数据库中的数据),否则你也不会获得任何性能增益。

之后你最好的选择就是将这些东西放入文件中。无论是BerkelyDB,NetCDF,HDF5,STXXL等。文件访问速度很慢,但这样做可以让您处理更多的数据,而不是放在内存中。

1

对于你所描述的,std :: vector并不是一个非常好的选择。这是一个与没有索引的表相当的SQL。最重要的是,用另一个容器的内容填充一个容器可能是一个合理的性能优化,但不是非常可读,也不太流行。有很多方法可以解决这个问题(IE,而不依赖托管代码.net)。

首选是选择一个更好的容器。如果你不需要稳定的迭代,那么你应该使用std :: set或std :: multi_set。这些容器使用平衡搜索树来按顺序存储值。这相当于所有列的简单SQL索引。

std::set<int> numbers; 
for (int i = -10; i <= 10; ++i) { 
    numbers.insert(i); 
} 

std::set::iterator first = numbers.find(1); 
std::set::iterator end = numbers.end(); 

现在你可以从first迭代,直到end不浪费任何额外的努力,在O(N日志(N))填充和O(日志(n))的寻求。迭代是(1)为std::set::iterator

如果由于某种原因,你必须使用一个向量,就可以得到更地道的C++使用std::find_if(见Max Lybbert's答案)

bool isPositive(int n) { return n > 0; } 

std::vector<int> numbers; 
for (int i = -10; i <= 10; ++i) { 
    numbers.push_back(i); 
} 

for (std::vector<int>::const_iterator end = numbers.end(), 
     iter = std::find_if(numbers.begin(), end, isPositive); // <- first positive value 
     iter != end; 
     iter = std::find_if(iter, end, isPositive) // <- advance iter to the next positive 
) { 

    // iter is guaranteed to be positive here, do something with it! 
} 

如果你想要的东西,甚至Ø在没有实际连接到数据库的情况下,更多地唤起SQL,你应该看看Boost,特别是容器和boost迭代器。