STL कंटेनर को छानने का आधुनिक तरीका?


102

C # के वर्षों के बाद C ++ में वापस आना मैं सोच रहा था कि आधुनिक क्या है - पढ़ें: C ++ 11 - एक सरणी फ़िल्टर करने का तरीका होगा, अर्थात हम इस Linq क्वेरी के समान कुछ कैसे प्राप्त कर सकते हैं:

var filteredElements = elements.Where(elm => elm.filterProperty == true);

तत्वों के एक वेक्टर को फ़िल्टर करने के लिए ( stringsइस प्रश्न के लिए)?

मुझे पूरी उम्मीद है कि पुराने एसटीएल शैली के एल्गोरिदम (या यहां तक ​​कि एक्सटेंशन जैसे boost::filter_iterator) स्पष्ट तरीकों को परिभाषित करने की आवश्यकता है जो अब तक डूब गए हैं?


क्या यह उन सभी तत्वों को पुनः प्राप्त करता है जो filterPropertyकरने के लिए सेट है true?
जोसेफ मैंसफील्ड

क्षमा करें, हाँ। कुछ सामान्य फ़िल्टर मानदंड ..
एटीवी

3
कुछ पुस्तकालय भी हैं जो .NET के LINQ तरीकों का अनुकरण करने की कोशिश करते हैं: Linq ++ और cpplinq । मैंने उनके साथ काम नहीं किया है लेकिन मेरा अनुमान है कि वे एसटीएल कंटेनरों का समर्थन करेंगे।
डिर्क

1
आपको इस बारे में अधिक स्पष्ट होना चाहिए कि आप क्या चाहते हैं, क्योंकि C ++ और C # दोनों में सक्षम लोगों का समूह छोटा है। वर्णन करें कि आप क्या करना चाहते हैं।
यक्क - एडम नेवरामॉन्ट

जवाबों:


124

इसके लिए cplusplus.com से उदाहरण देखें std::copy_if:

std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar;

// copy only positive numbers:
std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );

std::copy_iffooयहाँ पर हर तत्व के लिए लैम्ब्डा एक्सप्रेशन का मूल्यांकन करता है और अगर यह रिटर्न trueकरता है तो वैल्यू को कॉपी करता है bar

std::back_inserterहमें वास्तव में आवश्यक आकार में इसे आकार देने के बिना एक पुनरावृत्ति के साथ bar(उपयोग push_back()) के अंत में नए तत्वों को सम्मिलित करने की अनुमति देता है ।


30
क्या यह वास्तव में LINQ के सबसे नजदीक है जिसे C ++ को पेश करना है? यह उत्सुक है (IOW आलसी नहीं है), और बहुत ही क्रिया।
usr

1
@usr इसका IMO सिंथैटिक शुगर, लूप के लिए एक सरल काम करता है (और अक्सर एक को नकल से बचने की अनुमति देता है)।
सेबस्टियन हॉफमैन

1
OPs उदाहरण किसी भी LINQ सिंथैटिक शुगर का उपयोग नहीं करता है। लाभ आलसी मूल्यांकन और रचनाशीलता हैं।
usr

1
@usr जो अब भी एक साधारण लूप द्वारा आसानी से प्राप्त किया जा सकता है, फॉर-लूप से std::copy_ifअधिक नहीं है
सेबस्टियन हॉफमैन

15
@Paranaix सब कुछ विधानसभा के लिए सिर्फ चीनी कहा जा सकता है। बिंदु यह है कि छोरों के लिए नहीं लिखना है, जब एक एल्गोरिथ्म को स्पष्ट रूप से आदिम संचालन (जैसे फ़िल्टर) का उपयोग करके पठनीय तरीके से बनाया जा सकता है। कई भाषाएं ऐसी सुविधा प्रदान करती हैं - सी ++ में दुर्भाग्यवश यह अभी भी गुदगुदी है।
बार्टोसजप

48

एक अधिक कुशल दृष्टिकोण, यदि आपको वास्तव में सूची की एक नई प्रतिलिपि की आवश्यकता नहीं है, तो यह वास्तव remove_ifमें मूल कंटेनर से तत्वों को निकालता है।


7
@ATV मुझे remove_ifविशेष रूप से पसंद है क्योंकि यह म्यूटेशन की उपस्थिति में फिल्टर का उपयोग करने का तरीका है, जो कि पूरी नई सूची की प्रतिलिपि बनाने से तेज है। अगर मैं C ++ में फ़िल्टर कर रहा था copy_if, तो मैं इस पर उपयोग करूँगा , इसलिए मुझे लगता है कि यह जोड़ता है।
djhaskin987

16
वेक्टर के लिए, कम से कम, remove_ifनहीं बदलता है size()आपको उसके eraseलिए इसे चेन करना होगा
13

5
@ क्रैम्पियन हाँ .. मिटा / हटाओ। एक और सुंदरता जो मुझे अक्सर महसूस करती है कि मैं इन दिनों सी ++ (आधुनिक भाषाओं के विपरीत) में काम करते समय एक टेप में छेद कर रहा हूं ;-)
एटीवी

1
स्पष्ट मिटा एक विशेषता है। आपको सभी मामलों में मिटना नहीं है। कभी-कभी पुनरावृत्तियां जारी रखने के लिए पर्याप्त होती हैं। ऐसे मामलों में एक निहित मिटा अनावश्यक उपरि पैदा करेगा। इसके अलावा, नहीं हर कंटेनर resizable है। std :: array for example में कोई erase method नहीं है।
मार्टिन फहर्स

35

C ++ 20 में, रेंज लाइब्रेरी से फ़िल्टर व्यू का उपयोग करें: (आवश्यकता है #include <ranges>)

// namespace views = std::ranges::views;
vec | views::filter([](int a){ return a % 2 == 0; })

आलसी भी तत्वों में लौटता है vec

(देखें [range.adaptor.object] / 4 और [range.filter] )


यह पहले से ही जीसीसी 10 ( लाइव डेमो ) द्वारा समर्थित है । बजना और जीसीसी के पुराने संस्करणों के लिए, मूल सीमा-v3 पुस्तकालय भी इस्तेमाल किया जा सकता है, के साथ #include <range/v3/view/filter.hpp>(या #include <range/v3/all.hpp>) और ranges::viewsके बजाय नाम स्थान std::ranges::views( लाइव डेमो )।


आपको संकलन करने के लिए अपने जवाब के लिए आवश्यक नाम स्थान का उपयोग करना चाहिए। इसके अलावा, क्या संकलक आज के रूप में इसका समर्थन करता है?
17

2
@ अब बेहतर?
एलएफ

2
अगर कोई इसे macOS में करने की कोशिश करता है: मई 2020 तक, libc ++ इसका समर्थन नहीं करता है।
डेक्स

25

मुझे लगता है कि Boost.Range एक उल्लेख के भी हकदार हैं। परिणामी कोड मूल के बहुत करीब है:

#include <boost/range/adaptors.hpp>

// ...

using boost::adaptors::filtered;
auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; });

केवल नकारात्मक पहलू में लैम्बडा के पैरामीटर प्रकार को स्पष्ट रूप से घोषित करना है। मैंने घोषणापत्र (तत्वों) का उपयोग किया :: value_type क्योंकि यह सटीक प्रकार को बाहर करने से बचता है, और सामान्यता का एक दाना भी जोड़ता है। वैकल्पिक रूप से, C ++ 14 के बहुरूपी लंबोदर के साथ, प्रकार को केवल ऑटो के रूप में निर्दिष्ट किया जा सकता है:

auto filteredElements = elements | filtered([](auto const& elm)
    { return elm.filterProperty == true; });

फ़िल्टर किए गए परिक्षेत्र एक सीमा होगी, जो ट्रैवर्सल के लिए उपयुक्त है, लेकिन यह मूल कंटेनर का एक दृश्य है। यदि आपको आवश्यकता है तो मापदंड को संतुष्ट करने वाले तत्वों की प्रतियों से भरा एक और कंटेनर है (ताकि यह मूल कंटेनर के जीवनकाल से स्वतंत्र हो), यह इस तरह दिख सकता है:

using std::back_inserter; using boost::copy; using boost::adaptors::filtered;
decltype(elements) filteredElements;
copy(elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; }), back_inserter(filteredElements));

12

C # के लिए मेरा सुझाव C # के बराबर है

var filteredElements = elements.Where(elm => elm.filterProperty == true);

एक टेम्प्लेट फ़ंक्शन को परिभाषित करें, जिसे आप फ़िल्टर करने के लिए एक लैम्बडा विधेय पास करते हैं। टेम्पलेट फ़ंक्शन फ़िल्टर किए गए परिणाम को लौटाता है। उदाहरण के लिए:

template<typename T>
vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate)
{
  vector<T> result;
  copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
  return result;
}

उपयोग करने के लिए - एक तुच्छ उदाहरण देते हुए:

std::vector<int> mVec = {1,4,7,8,9,0};

// filter out values > 5
auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); });

// or > target
int target = 5;
auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });

11

अंडरस्कोर-डी सुझावों के बाद बेहतर pjm कोड :

template <typename Cont, typename Pred>
Cont filter(const Cont &container, Pred predicate) {
    Cont result;
    std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate);
    return result;
}

उपयोग:

std::vector<int> myVec = {1,4,7,8,9,0};

auto filteredVec = filter(myVec, [](int a) { return a > 5; });
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.