2009-07-01 72 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迭代器。