क्यों std :: सेट में "समाहित" सदस्य फ़ंक्शन नहीं है?


103

मैं बहुत अधिक उपयोग कर रहा हूं std::set<int>और अक्सर मुझे यह जांचना पड़ता है कि क्या इस तरह के सेट में नंबर है या नहीं।

मुझे यह लिखना स्वाभाविक लगेगा:

if (myset.contains(number))
   ...

लेकिन एक containsसदस्य की कमी के कारण , मुझे बोझिल लिखना होगा:

if (myset.find(number) != myset.end())
  ..

या नहीं के रूप में स्पष्ट:

if (myset.count(element) > 0) 
  ..

क्या इस डिजाइन निर्णय का कोई कारण है?


7
अधिकांश मानक पुस्तकालय पुनरावृत्तियों के साथ काम करते हैं इसलिए आम तौर पर पुनरावृत्तियों को वापस करने के लिए कार्य करते हैं जो आप उम्मीद करेंगे। कठिन नहीं है, हालांकि एक फ़ंक्शन लिखने के लिए दूर है। सबसे अधिक संभावना है कि संकलक इसे इनलाइन करेगा क्योंकि यह केवल एक पंक्ति या कोड का 2 होना चाहिए और आपको समान प्रदर्शन मिलेगा।
नाथनऑलिवर

3
count()दृष्टिकोण के साथ एक और (अधिक मौलिक) समस्या यह है कि यह एक से अधिक काम countains()करना होगा।
सिंह हिंसार

11
मौलिक कारण है कि डिजाइन फैसले के पीछे वह यह है कि contains()जो रिटर्न एक boolहैं जहां तत्व संग्रह में है के बारे में महत्वपूर्ण जानकारी खोfind()उस सूचना को पुनरावृत्तिकर्ता के रूप में संरक्षित और लौटाता है, इसलिए STL जैसी सामान्य लाइब्रेरी के लिए एक बेहतर विकल्प है। (यह कहने के लिए नहीं है कि bool contains()एक बहुत अच्छा नहीं है, या यहां तक ​​कि आवश्यक है, हालांकि।)
लियो हिंसार

3
contains(set, element)सेट के सार्वजनिक इंटरफ़ेस का उपयोग करके एक निशुल्क फ़ंक्शन लिखना आसान है । इसलिए, सेट का इंटरफ़ेस कार्यात्मक रूप से पूर्ण है; एक सुविधा विधि जोड़ने से किसी भी अतिरिक्त फ़ंक्शन को सक्षम किए बिना इंटरफ़ेस बढ़ जाता है, जो कि C ++ तरीका नहीं है।
टोबे स्पीट

3
क्या हम इन दिनों सब कुछ बंद कर रहे हैं? यह प्रश्न किसी भी तरह से "मुख्य रूप से राय आधारित" कैसे है?
श्री एलियन

जवाबों:


148

मुझे लगता है कि यह शायद था क्योंकि वे बनाने के लिए कोशिश कर रहे थे std::setऔर std::multisetसंभव के रूप में समान। (और जाहिर countहै के लिए एक पूरी तरह से समझदार अर्थ है std::multiset।)

व्यक्तिगत रूप से मुझे लगता है कि यह एक गलती थी।

यह बहुत बुरा नहीं लगता है अगर आप दिखावा करते हैं कि countसिर्फ एक गलत वर्तनी है containsऔर परीक्षण को इस प्रकार लिखें:

if (myset.count(element)) 
   ...

हालांकि यह अभी भी शर्म की बात है।


5
संयोग से, यह नक्शों और मल्टीमैप्स के साथ बिल्कुल वैसा ही है (जो कि बस उतना ही बदसूरत है, लेकिन इन सभी तुलनाओं से कम बदसूरत है .end())।
Matteo इटालिया

8
वैकल्पिक रूप से, उन्हें contains()इस आधार पर एक अतिरिक्त सदस्य की कोई आवश्यकता नहीं दिख रही होगी कि यह निरर्थक होगा क्योंकि किसी भी के लिए std::set<T> sऔर T t, का परिणाम s.contains(t)बिल्कुल समान है static_cast<bool>(s.count(t))। एक सशर्त अभिव्यक्ति में मूल्य का उपयोग करने के लिए इसे संक्षेप में प्रस्तुत किया जाएगा bool, उन्होंने महसूस किया हो सकता है कि count()इस उद्देश्य को अच्छी तरह से पूरा किया।
जस्टिन टाइम -

2
गलत वर्तनी? if (myset.ICanHaz(element)) ...: डी
स्टीफन गौरीचॉन

3
@MartinBonner यह वास्तव में कोई फर्क नहीं पड़ता अगर इसे छोड़ने के कारणों को गूंगा था। यह भी वास्तव में कोई फर्क नहीं पड़ता अगर बातचीत 100% अंतिम तर्क नहीं थी। यहां आपका जवाब सिर्फ एक उचित अनुमान है कि आप कैसे सोचते हैं कि यह होना चाहिए। बातचीत, और किसी से जवाब न केवल इसमें शामिल है, लेकिन इसे प्रस्तावित करने का काम सौंपा (भले ही वे नहीं थे) इस अनुमान की तुलना में सच्चाई के करीब है, चाहे आप इसे कैसे भी देखें। नंगे न्यूनतम पर आपको कम से कम इस उत्तर में इसका उल्लेख करना चाहिए, यह एक महान सुधार होगा और ऐसा करने के लिए जिम्मेदार बात होगी।
जेसन सी

2
@JasonC: क्या आप आगे बढ़ सकते हैं और कृपया नीचे एक अनुभाग संपादित करें? मैं वास्तव में उस बिंदु को नहीं समझता हूं जिसे आप बनाने की कोशिश कर रहे हैं, और टिप्पणी शायद यह स्पष्ट करने का सबसे अच्छा तरीका नहीं है। धन्यवाद!
मार्टिन बोनर

44

लिखने में सक्षम होने के लिए if (s.contains()), contains()एक bool(या एक प्रकार परिवर्तनीय bool, जो एक और कहानी है) को वापस करना होगा, जैसे कि binary_searchकरता है।

मौलिक कारण डिजाइन फैसले के पीछे नहीं यह इस तरह से करने के लिए वह यह है कि contains()जो रिटर्न एक boolहैं जहां तत्व संग्रह में है के बारे में महत्वपूर्ण जानकारी खोfind()उस सूचना को पुनरावृत्तिकर्ता के रूप में संरक्षित और लौटाता है, इसलिए STL जैसी सामान्य लाइब्रेरी के लिए एक बेहतर विकल्प है। एलेक्स स्टेपानोव के लिए यह हमेशा मार्गदर्शक सिद्धांत रहा है, क्योंकि उन्होंने अक्सर (उदाहरण के लिए, यहाँ ) समझाया है ।

count()सामान्य रूप से दृष्टिकोण के रूप में, हालांकि यह अक्सर एक ठीक समाधान है, इसके साथ समस्या यह है कि यह एक से अधिक काम contains() करना होगा

यह कहना नहीं है कि यह bool contains()बहुत अच्छा नहीं है या आवश्यक भी नहीं है। कुछ समय पहले हमने ISO C ++ Standard - Future Proposals समूह में इसी मुद्दे पर एक लंबी चर्चा की थी ।


5
और यह ध्यान रखना दिलचस्प है कि यह चर्चा इस पर सर्वसम्मति के साथ समाप्त हो गई कि यह वांछनीय है और आपको इसके लिए एक प्रस्ताव लिखने के लिए कहा जा रहा है।
20

@PJTraill ट्रू, और जो कारण मैं आगे नहीं बढ़ा वह यह है कि contains(), जाहिर है, मौजूदा कंटेनरों और एल्गोरिदम के साथ दृढ़ता से बातचीत करेंगे, जो अवधारणाओं और श्रेणियों द्वारा दृढ़ता से प्रभावित होंगे - उस समय C ++ 17 में आने की उम्मीद है - और मैं आश्वस्त था (चर्चा के परिणामस्वरूप और साथ ही निजी ईमेल एक्सचेंजों के एक जोड़े के रूप में) कि पहले उनके लिए इंतजार करना बेहतर था। बेशक, 2015 में यह स्पष्ट नहीं था कि न तो अवधारणाएं और न ही श्रेणियाँ इसे सी ++ 17 में नहीं बनाएंगी (वास्तव में, उच्च उम्मीदें थीं कि वे)। मुझे यकीन नहीं है कि यह अब इसे आगे बढ़ाने लायक है, हालांकि।
सिंह हिंसार

1
के लिए std::set(जो सवाल के बारे में पूछता है), मैं नहीं देखता कि कैसे countअधिक से अधिक काम containsकरना होगा। count(लगभग) का glibc कार्यान्वयन है return find(value) == end() ? 0 : 1;। टर्नरी ऑपरेटर बनाम रिटर्निंग के बारे में विवरण के अलावा != end()(जो मैं आशावादी को हटाने की उम्मीद करूंगा), मैं यह नहीं देख सकता कि कोई और काम कैसे हो।
मार्टिन बोनर

4
"... () में () जो एक बूल लौटाता है, उस तत्व के बारे में बहुमूल्य जानकारी खो देगा जहां तत्व संग्रह में है " - यदि उपयोगकर्ता कहता है myset.contains()(यदि यह मौजूद है), तो यह एक बहुत मजबूत संकेत होगा कि वह जानकारी मूल्यवान नहीं है ( उस संदर्भ में उपयोगकर्ता को)।
कीथ थॉम्पसन

1
क्यों करता है count()और अधिक से अधिक काम करने के contains()लिए क्या करना होगा std::set? यह अद्वितीय है इसलिए count()बस return contains(x) ? 1 : 0;वही हो सकता है जो बिल्कुल वैसा ही हो।
टिम्मम

22

इसका अभाव है क्योंकि किसी ने इसे नहीं जोड़ा। किसी ने इसे इसलिए नहीं जोड़ा क्योंकि एसटीएल के जिन कंटेनरों को stdलाइब्रेरी में शामिल किया गया था, वे इंटरफ़ेस में न्यूनतम हैं। (ध्यान दें कि std::stringएसटीएल से उसी तरह नहीं आया था)।

यदि आपको कुछ अजीब सिंटैक्स का बुरा नहीं लगता है, तो आप इसे नकली कर सकते हैं:

template<class K>
struct contains_t {
  K&& k;
  template<class C>
  friend bool operator->*( C&& c, contains_t&& ) {
    auto range = std::forward<C>(c).equal_range(std::forward<K>(k));
    return range.first != range.second;
    // faster than:
    // return std::forward<C>(c).count( std::forward<K>(k) ) != 0;
    // for multi-meows with lots of duplicates
  }
};
template<class K>
containts_t<K> contains( K&& k ) {
  return {std::forward<K>(k)};
}

उपयोग:

if (some_set->*contains(some_element)) {
}

मूल रूप से, आप stdइस तकनीक का उपयोग करके अधिकांश C ++ प्रकारों के लिए विस्तार विधियाँ लिख सकते हैं ।

यह सिर्फ यह करने के लिए एक बहुत अधिक समझ में आता है:

if (some_set.count(some_element)) {
}

लेकिन मैं विस्तार विधि विधि से चकित हूं।

वास्तव में दुखद बात यह है कि एक कुशल लेखन या तो containsतेजी से हो सकता है multimapया multiset, जैसा कि उन्हें सिर्फ एक तत्व ढूंढना है, जबकि countउनमें से प्रत्येक को खोजना होगा और उन्हें गिनना होगा

7 की 1 बिलियन प्रतियों वाली एक मल्टीसेट (आप जानते हैं, यदि आप रन आउट होते हैं) वास्तव में धीमी .count(7)हो सकती है, लेकिन बहुत तेज़ हो सकती है contains(7)

उपरोक्त विस्तार विधि के साथ, हम इस मामले के लिए तेजी से बना सकते हैं, इसका उपयोग lower_boundकरके end, और फिर तत्व की तुलना करके। ऐसा करने के लिए एक अनियंत्रित म्याऊ के साथ-साथ एक आदेशित म्याऊ के लिए फैंसी SFINAE या कंटेनर-विशिष्ट ओवरलोड की आवश्यकता होगी।


2
7 की 1 बिलियन प्रतियां? और यहाँ मुझे लगा कि std::setइसमें डुप्लिकेट नहीं हो सकते हैं और इसलिए std::set::countयह हमेशा वापस आएगा 0या 1
nwp

5
@nwp std::multiset::countकर सकते हैं
milleniumbug

2
@nwp backticks"सेट" शब्द के आसपास मेरी कमी है क्योंकि मैं std::setविशेष रूप से बात नहीं कर रहा हूँ । आपको बेहतर महसूस कराने के लिए, मैं
याक - एडम नेवरामोंट

3
मुझे लगता है कि जो भी "म्याऊ" के लिए एक संदर्भ होना चाहिए था के लिए मजाक याद आ रहा है।
user2357112

2
@ user2357112 म्याऊ "सेट या मैप" के लिए एक प्लेसहोल्डर है। एसटीएल क्यों पूछें ।
यक - एडम नेवरामॉन्ट

12

आप विशेष मामले में देख रहे हैं और बड़ी तस्वीर नहीं देख रहे हैं। जैसा कि प्रलेखन में कहा गया है कि एसोसिएटिवकॉन्सेटर अवधारणा std::setकी आवश्यकता पूरी होती है । उस अवधारणा के लिए इसका कोई मतलब नहीं है , क्योंकि यह बहुत बेकार है और , लेकिन उन सभी के लिए ठीक काम करता है। हालांकि विधि के लिए एक उपनाम के रूप में जोड़ा जा सकता है के लिए , और उनके टुकड़ों में बंटी संस्करणों (जैसे के लिए में ), लेकिन पुस्तकालय रचनाकारों की तरह दिखता है इसके लिए वास्तविक आवश्यकता नहीं देखा था।containsstd::multisetstd::multimapcountcontainscountstd::setstd::maplengthsize()std::string


8
ध्यान दें कि stringयह एक राक्षस है: यह एसटीएल से पहले मौजूद था, जहां यह था lengthऔर उन सभी तरीकों को जो सूचकांक आधारित हैं, और फिर एसटीएल मॉडल के भीतर फिट होने के लिए "कंटेनरीकृत" किया गया था ... पिछड़े संगतता कारणों के लिए मौजूदा तरीकों को हटाए बिना। । GotW # 84 देखें : मोनोलिथ्स अनस्ट्रुंग => stringवास्तव में "सदस्य कार्यों की न्यूनतम राशि" डिजाइन सिद्धांत का उल्लंघन करता है।
मैथ्यू एम।

5
लेकिन फिर सवाल यह हो जाता है कि "इस तरह से एसोसिएटिवकॉन्सेटर अवधारणा क्यों है?" - और मुझे यकीन नहीं है कि यह हिंडाइट था।
मार्टिन बोनर

24
यह पूछने पर कि क्या एक मल्टीसेट, एक मल्टीमैप या मैप में कुछ है जो मेरे लिए सही अर्थ रखता है। वास्तव में, containsएक सेट / मानचित्र पर प्रयास में समान है, लेकिन एक मल्टीसेट / मल्टीमैप की तुलना में तेजी से बनाया जा सकता है count
यक - एडम नेवरामोंट

5
AssociativeContainer को किसी विधि की आवश्यकता नहीं है contains
user2357112

6
@ स्लाव यह कहने जैसा है size()और empty()डुप्लिकेट हैं, फिर भी कई कंटेनरों में दोनों हैं।
बैरी

10

हालांकि मुझे नहीं पता कि क्यों std::setनहीं है, containsलेकिन countजो केवल कभी लौटता है 0या 1, आप containsइस तरह एक अस्थायी हेल्पर फ़ंक्शन लिख सकते हैं :

template<class Container, class T>
auto contains(const Container& v, const T& x)
-> decltype(v.find(x) != v.end())
{
    return v.find(x) != v.end();
}

और इसे इस तरह से उपयोग करें:

    if (contains(myset, element)) ...

3
-1, क्योंकि यह सीधे तौर पर इस तथ्य से विरोधाभास है कि वास्तव में containsविधि मौजूद है, यह सिर्फ एक बेवकूफ तरीके से नाम दिया गया है।
मट्टियो इटालिया

4
"एसटीएल का प्रयास एक न्यूनतम इंटरफ़ेस पेशकश करने के लिए" चाफ़ std::string खांसी
bolov

6
@ प्रतीक: आपकी बात? std.::stringएसटीएल का हिस्सा नहीं है! यह मानक पुस्तकालय का हिस्सा है और पूर्वव्यापी रूप से
अस्थायी

3
@MatteoItalia countधीमी हो सकती है क्योंकि इसे प्रभावी ढंग findसे शुरुआत और अंत की समाप्ति के लिए दो एस करने की आवश्यकता होती है यदि कोड के साथ साझा किया गया हो multiset
मार्क रैनसम

2
ओपी पहले से ही जानता है कि यह बेमानी है, लेकिन स्पष्ट रूप से कोड को स्पष्ट रूप से पढ़ना चाहता है contains। मुझे उस में कुछ भी गलत नहीं दिखता है। @MarkRansom थोड़ा SFINAE इस टेम्प्लेट को उन चीजों से बांधने से रोकने के लिए है, जो इसे नहीं करना चाहिए।
रस्टीक्स

7

setमेरे लिए सही कारण मेरे लिए एक रहस्य है, लेकिन इस डिजाइन के लिए एक संभावित स्पष्टीकरण mapलोगों को दुर्घटना से अयोग्य लिखने से रोक सकता है:

if (myMap.contains("Meaning of universe"))
{
    myMap["Meaning of universe"] = 42;
}

जिसके परिणामस्वरूप दो mapलुकअप होंगे।

इसके बजाय, आप एक पुनरावृत्त पाने के लिए मजबूर हैं। यह आपको एक मानसिक संकेत देता है कि आपको पुनरावृत्त का पुन: उपयोग करना चाहिए:

auto position = myMap.find("Meaning of universe");
if (position != myMap.cend())
{
    position->second = 42;
}

जो केवल एक ही mapखोज का उपभोग करता है ।

जब हमें एहसास होता है setऔर mapउसी मांस से बने होते हैं, तो हम इस सिद्धांत को भी लागू कर सकते हैं set। यही है, अगर हम किसी वस्तु पर कार्य करना चाहते हैं setयदि वह मौजूद है set, तो यह डिज़ाइन हमें इस प्रकार कोड लिखने से रोक सकता है:

struct Dog
{
    std::string name;
    void bark();
}

operator <(Dog left, Dog right)
{
    return left.name < right.name;
}

std::set<Dog> dogs;
...
if (dogs.contain("Husky"))
{
    dogs.find("Husky")->bark();
}

बेशक यह सब केवल एक अटकल है।


1
हां, लेकिन किलों के सेट के लिए यह लागू नहीं होता है।
Jabberwocky

7
सिवाय लोगों के if (myMap.count("Meaning of universe")), बस इतना ही ठीक लिख सकता हूँ , तो ...?
बैरी

@MichaelWalz ओह, तुम सही हो। मैंने अपना उत्तर भी संशोधित किया जिसमें सेट उदाहरण भी शामिल है। हालाँकि, एक सेट के लिए तर्क के लिए तर्क मेरे लिए एक रहस्य है।
मार्टिन डोज़र्डिक

2
यह सही नहीं हो सकता। वे बस के रूप में आसानी से के साथ अपने अक्षम कोड लिख सकते हैं containsके साथ के रूप में count
मार्टिन बोनर


0

बाइनरी_सर्च के बारे में क्या?

 set <int> set1;
 set1.insert(10);
 set1.insert(40);
 set1.insert(30);
 if(std::binary_search(set1.begin(),set1.end(),30))
     bool found=true;

यह काम नहीं करेगा std::unordered_set, लेकिन इसके लिए std::setयह होगा।
Jabberwocky

यह सामान्य है, बाइनरी_सर्च सिर्फ बाइनरी पेड़ों के लिए काम करता है।
मैसिमिलियानो डि कैविओ

0

इसमें () को एक बूल वापस करना होगा। C ++ 20 संकलक के उपयोग से मुझे कोड के लिए निम्न आउटपुट मिलता है:

#include<iostream>
#include<map>
using namespace std;

int main()
{
    multimap<char,int>mulmap;
    mulmap.insert(make_pair('a', 1)); //multiple similar key
    mulmap.insert(make_pair('a', 2)); //multiple similar key
    mulmap.insert(make_pair('a', 3)); //multiple similar key
    mulmap.insert(make_pair('b', 3));
    mulmap.insert({'a',4});
    mulmap.insert(pair<char,int>('a', 4));
    
    cout<<mulmap.contains('c')<<endl;  //Output:0 as it doesn't exist
    cout<<mulmap.contains('b')<<endl;  //Output:1 as it exist
}

-1

एक और कारण यह है कि यह एक प्रोग्रामर को गलत धारणा देगा कि std :: set गणित सेट सिद्धांत अर्थ में एक सेट है। यदि वे इसे लागू करते हैं, तो कई अन्य प्रश्न अनुसरण करेंगे: यदि एक मान के लिए एक std :: set में () शामिल है, तो दूसरे सेट के लिए ऐसा क्यों नहीं है? कहां हैं संघ (), चौराहे () और अन्य सेट संचालन और विधेय?

जवाब, निश्चित रूप से, कि कुछ सेट ऑपरेशन पहले से ही (std :: set_union () इत्यादि) के कार्यों के रूप में कार्यान्वित किए जाते हैं और अन्य शामिल हैं जैसा कि शामिल है ()। ऑब्जेक्ट सदस्यों की तुलना में फ़ंक्शंस और फ़ंक्शन ऑब्जेक्ट गणित सार के साथ बेहतर काम करते हैं, और वे विशेष कंटेनर प्रकार तक सीमित नहीं हैं।

यदि किसी को पूर्ण गणित-सेट की कार्यक्षमता को लागू करने की आवश्यकता है, तो उसके पास न केवल अंतर्निहित कंटेनर का एक विकल्प है, बल्कि उसके पास कार्यान्वयन विवरणों का भी विकल्प है, उदाहरण के लिए, उसकी थ्योरी_यूनियन () फ़ंक्शन अपरिवर्तनीय वस्तुओं के साथ काम करेगी, कार्यात्मक प्रोग्रामिंग के लिए बेहतर अनुकूल है। , या यह अपने ऑपरेंड को संशोधित करेगा और मेमोरी को बचाएगा? क्या इसे प्रारंभ से फ़ंक्शन ऑब्जेक्ट के रूप में लागू किया जाएगा या इसे लागू करना बेहतर होगा C- फ़ंक्शन, और std का उपयोग करें :: फ़ंक्शन <यदि आवश्यक हो?

जैसा कि यह अब है, std :: set सिर्फ एक कंटेनर है, गणित के अर्थ में सेट के कार्यान्वयन के लिए अच्छी तरह से अनुकूल है, लेकिन यह लगभग एक सैद्धांतिक सेट के रूप में दूर होने के रूप में दूर है :: वेक्टर एक सैद्धांतिक वेक्टर होने से।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.