C ++ मानक पुस्तकालय में कोई transform_if क्यों नहीं है?


83

जब एक आकस्मिक प्रतिलिपि (1. के साथ संभव copy_if) करने के लिए एक उपयोग मामला उभरा, लेकिन उन मूल्यों के लिए संकेत के एक कंटेनर के लिए मूल्यों के एक कंटेनर से (2. साथ करने योग्य transform)।

उपलब्ध उपकरणों के साथ मैं इसे दो चरणों से कम में नहीं कर सकता :

#include <vector>
#include <algorithm>

using namespace std;

struct ha { 
    int i;
    explicit ha(int a) : i(a) {}
};

int main() 
{
    vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector
    // GOAL : make a vector of pointers to elements with i < 2
    vector<ha*> ph; // target vector
    vector<ha*> pv; // temporary vector
    // 1. 
    transform(v.begin(), v.end(), back_inserter(pv), 
        [](ha &arg) { return &arg; }); 
    // 2. 
    copy_if(pv.begin(), pv.end(), back_inserter(ph),
        [](ha *parg) { return parg->i < 2;  }); // 2. 

    return 0;
}

बेशक हम कह सकते हैं remove_ifपर pvऔर एक अस्थायी की आवश्यकता को समाप्त, बेहतर अभी तक हालांकि, यह नहीं मुश्किल करने के लिए लागू (एकल संचालन के लिए) कुछ इस तरह:

template <
    class InputIterator, class OutputIterator, 
    class UnaryOperator, class Pred
>
OutputIterator transform_if(InputIterator first1, InputIterator last1,
                            OutputIterator result, UnaryOperator op, Pred pred)
{
    while (first1 != last1) 
    {
        if (pred(*first1)) {
            *result = op(*first1);
            ++result;
        }
        ++first1;
    }
    return result;
}

// example call 
transform_if(v.begin(), v.end(), back_inserter(ph), 
[](ha &arg) { return &arg;      }, // 1. 
[](ha &arg) { return arg.i < 2; });// 2.
  1. क्या उपलब्ध C ++ मानक लाइब्रेरी टूल के साथ अधिक सुरुचिपूर्ण वर्कअराउंड है?
  2. क्या transform_ifपुस्तकालय में मौजूद नहीं होने का एक कारण है ? क्या मौजूदा साधनों का संयोजन पर्याप्त वर्तन और / या माना जाने वाला प्रदर्शन अच्छा है?

(IMO) का transform_ifअर्थ है "केवल एक निश्चित विधेय संतुष्ट होने पर परिवर्तन।" जो आप चाहते हैं उसके लिए एक अधिक वर्णनात्मक नाम copy_if_and_transform!
ओलिवर चार्ल्सवर्थ

@OliCharlesworth, वास्तव में copy_if"केवल एक निश्चित विधेय संतुष्ट होने पर कॉपी" का अर्थ है। यह उतना ही अस्पष्ट है।
शाहबाज

@ शहबाज: लेकिन यह क्या copy_ifकरता है, है ना?
ओलिवर चार्ल्सवर्थ

2
मुझे आश्चर्य नहीं होगा यदि इस तरह के नाम के बारे में विवाद इसे लागू नहीं करने के लिए एक कारण थे !!
निकोस अथानासीउ

6
शायद मैं इन टिप्पणियों में कुछ याद कर रहा हूं, लेकिन transform_ifसंभवतः उन तत्वों की प्रतिलिपि कैसे बना सकता है जो इसे रूपांतरित नहीं करते हैं, यदि परिवर्तन एक अलग असंगत प्रकार का हो सकता है? प्रश्न में कार्यान्वयन ठीक वही है जो मैं इस तरह के कार्य को करने की अपेक्षा करूंगा।

जवाबों:


33

मानक पुस्तकालय प्राथमिक एल्गोरिदम का पक्षधर है।

यदि संभव हो तो कंटेनर और एल्गोरिदम एक दूसरे से स्वतंत्र होना चाहिए।

इसी तरह, एल्गोरिदम जो मौजूदा एल्गोरिदम से बना हो सकता है, केवल शॉर्टहैंड के रूप में शामिल हैं।

यदि आपको एक परिवर्तन की आवश्यकता है, तो आप इसे तुच्छ रूप से लिख सकते हैं। यदि आप इसे / आज / चाहते हैं, रेडी-मड्स की रचना और ओवरहेड नहीं, तो आप एक श्रेणी लाइब्रेरी का उपयोग कर सकते हैं जिसमें आलसी रेंज हैं , जैसे Boost.Range , उदा:

v | filtered(arg1 % 2) | transformed(arg1 * arg1 / 7.0)

जैसा कि @hvd एक टिप्पणी में बताते हैं, transform_ifएक अलग प्रकार में दोहरा परिणाम ( double, इस मामले में)। रचना क्रम मायने रखता है, और बूस्ट रेंज के साथ आप भी लिख सकते हैं:

 v | transformed(arg1 * arg1 / 7.0) | filtered(arg1 < 2.0)

विभिन्न शब्दार्थों के परिणामस्वरूप। यह घर को ड्राइव करता है:

यह बहुत कम समझ में आता है शामिल करने के लिए std::filter_and_transform, std::transform_and_filter, std::filter_transform_and_filterमानक पुस्तकालय में आदि आदि

कोलिरु पर एक नमूना देखें

#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>

using namespace boost::adaptors;

// only for succinct predicates without lambdas
#include <boost/phoenix.hpp>
using namespace boost::phoenix::arg_names;

// for demo
#include <iostream>

int main()
{
    std::vector<int> const v { 1,2,3,4,5 };

    boost::copy(
            v | filtered(arg1 % 2) | transformed(arg1 * arg1 / 7.0),
            std::ostream_iterator<double>(std::cout, "\n"));
}

28
खैर, समस्या यह है कि मानक एल्गोरिदम को आसानी से नहीं बनाया जा सकता है, क्योंकि वे आलसी नहीं हैं।
Jan Hudec

1
@ जानहुडेक वास्तव में। (उसके लिए माफ़ करना? :))। यही कारण है कि आप एक पुस्तकालय का उपयोग करते हैं (जैसे कि आप संक्षिप्तता के लिए एएमपी / टीबीबी का उपयोग करते हैं, या सी # में प्रतिक्रियात्मक एक्सटेंशन)। कई लोग मानक में शामिल करने के लिए एक सीमा प्रस्ताव + कार्यान्वयन पर काम कर रहे हैं।
सेह

2
@sehe +1 बहुत प्रभावशाली है, मैंने आज कुछ नया सीखा है! क्या आप हमें बताने के लिए इतने दयालु हैं कि Boost.Range और फीनिक्स से परिचित नहीं हैं, जहाँ हम ऐसे दस्तावेज़ / उदाहरण पा सकते हैं जो बताते हैं कि कैसे boost::phoenixबिना लैम्बदास के इस तरह के अच्छे विधेय बनाने के लिए उपयोग किया जाए? एक त्वरित Google खोज ने कुछ भी प्रासंगिक नहीं लौटाया। धन्यवाद!
अली

1
मैं "इसे शामिल करने के लिए बहुत कम समझ में आता है std :: filter_and_transform" भाग को लेकर असहमत हूं। अन्य प्रोग्रामिंग भाषाएं अपने "मानक पुस्तकालय" में भी यह संयोजन प्रदान करती हैं। यह पूरी तरह से एक बार तत्वों की सूची पर पुनरावृति करने के लिए समझ में आता है, उन्हें मक्खी पर बदलना, जबकि उन लोगों को छोड़ देना जो रूपांतरित नहीं हो सकते। अन्य दृष्टिकोणों में एक से अधिक पास की आवश्यकता होती है। हां, आप BOOST का उपयोग कर सकते हैं, लेकिन वास्तव में सवाल यह था कि "C ++ मानक पुस्तकालय में कोई transform_if क्यों नहीं है?"। और IMHO, वह यह सवाल करने के लिए सही है। मानक पुस्तकालय में ऐसा कार्य होना चाहिए।
जॉनी डी

1
@sehe के बारे में "वे सभी रचना योग्य सार का उपयोग करते हैं": यह सच नहीं है। उदाहरण के लिए जंग, बिल्कुल ऐसी है transform_if। इसे कहते हैं filter_map। हालाँकि, मुझे यह स्वीकार करना होगा कि यह कोड को सरल बनाने के लिए है, लेकिन दूसरी तरफ, C ++ मामले में एक ही तर्क लागू कर सकता है।
जॉनी डे

6

कई मायनों में लूप नोटेशन के लिए नया, एल्गोरिदम की आवश्यकता को कम करता है जो संग्रह के हर तत्व तक पहुंचता है जहां अब सिर्फ लूप लिखने के लिए क्लीनर है और तर्क को निष्क्रिय कर दिया है।

std::vector< decltype( op( begin(coll) ) > output;
for( auto const& elem : coll )
{
   if( pred( elem ) )
   {
        output.push_back( op( elem ) );
   }
}

क्या यह वास्तव में एक एल्गोरिथ्म में डालने के लिए बहुत अधिक मूल्य प्रदान करता है? जब भी हां, एल्गोरिथ्म C ++ 03 के लिए उपयोगी होता और वास्तव में मेरे पास इसके लिए एक था, हमें इसे जोड़ने में अब कोई वास्तविक लाभ की आवश्यकता नहीं है।

ध्यान दें कि व्यावहारिक उपयोग में आपका कोड हमेशा वैसा ही नहीं दिखेगा: या तो आपके पास "ओप" और "पूर्व" फ़ंक्शन नहीं हैं और उन्हें एल्गोरिदम में "फिट" बनाने के लिए लैम्ब्डा बनाना पड़ सकता है। यदि तर्क जटिल है, तो चिंताओं को अलग करना अच्छा है, अगर यह इनपुट प्रकार से एक सदस्य को निकालने और उसके मूल्य की जांच करने या संग्रह में जोड़ने की बात है, तो एल्गोरिथ्म का उपयोग करने की तुलना में यह एक बार फिर बहुत सरल है।

इसके अलावा, एक बार जब आप किसी प्रकार के ट्रांसफ़ॉर्म_आईएफ को जोड़ रहे हैं, तो आपको यह तय करना होगा कि ट्रांसफॉर्मेशन से पहले या बाद में प्रेडिक्टेट लागू करना है, या यहां तक ​​कि 2 प्रेडिकेट्स भी हैं और इसे दोनों जगहों पर लागू करना है।

तो हम क्या करने जा रहे हैं? 3 एल्गोरिदम जोड़ें? (और इस मामले में कि संकलक कन्वर्ट के दोनों छोर पर विधेय को लागू कर सकता है, एक उपयोगकर्ता गलती से गलत एल्गोरिथ्म आसानी से चुन सकता है और कोड अभी भी संकलित करता है लेकिन गलत परिणाम उत्पन्न करता है)।

इसके अलावा, यदि संग्रह बड़ा है, तो क्या उपयोगकर्ता पुनरावृत्तियों या मानचित्र / लूप के साथ लूप करना चाहता है? नक्शे की शुरूआत / कम करने से आपको समीकरण में और भी जटिलताएं मिलती हैं।

अनिवार्य रूप से, पुस्तकालय उपकरण प्रदान करता है, और उपयोगकर्ता को यह उपयोग करने के लिए उन्हें छोड़ दिया जाता है कि वे क्या करना चाहते हैं, न कि दूसरे तरीके से गोल करें जैसा कि अक्सर एल्गोरिदम के साथ होता था। (देखें कि उपर्युक्त उपयोगकर्ता ने चीजों का उपयोग करने के लिए चीजों को मोड़ने की कोशिश की है जो कि वे वास्तव में करना चाहते थे)।

एक सरल उदाहरण के लिए, एक नक्शा। प्रत्येक तत्व के लिए मैं मूल्य का उत्पादन करूँगा यदि कुंजी भी है।

std::vector< std::string > valuesOfEvenKeys
    ( std::map< int, std::string > const& keyValues )
{
    std::vector< std::string > res;
    for( auto const& elem: keyValues )
    {
        if( elem.first % 2 == 0 )
        {
            res.push_back( elem.second );
        }
    }
    return res;
}         

अच्छा और सरल। फैंसी फिटिंग जो एक ट्रांसफॉर्म_इफ एल्गोरिथ्म में है?


4
अगर आपको लगता है कि ऊपर दिए गए मेरे कोड में 2 लैम्ब्डा के साथ एक ट्रांसफॉर्म_आईएफ की तुलना में त्रुटियों के लिए अधिक जगह है, एक विधेय के लिए और एक ट्रांसफॉर्मेशन के लिए, तो कृपया इसे समझाएं। असेंबली, C और C ++ अलग-अलग भाषाएं हैं और अलग-अलग जगह हैं। एक ही स्थान पर एल्गोरिथ्म एक स्थान पर हो सकता है एक लूप के ऊपर "मैप / कम" करने की क्षमता है, इसलिए बड़े संग्रह पर समवर्ती रूप से चलाएं। हालांकि इस तरह से उपयोगकर्ता नियंत्रित कर सकता है कि अनुक्रम में लूप करना है या मैप-कम करना है।
कैशबैक

3
एक उचित कार्यात्मक दृष्टिकोण में विधेय और उत्परिवर्ती के लिए कार्य अच्छी तरह से परिभाषित ब्लॉक हैं जो निर्माण को ठीक से संरचित करते हैं। लूप बॉडी के लिए इसमें मनमानी चीजें हो सकती हैं, और आपके द्वारा देखे जाने वाले प्रत्येक लूप को इसके व्यवहार को समझने के लिए सावधानीपूर्वक विश्लेषण करना होगा।
बार्टेक बानचेविच

2
उचित कार्यात्मक भाषाओं के लिए उचित कार्यात्मक दृष्टिकोण छोड़ दें। यह C ++ है।
कैशबैक

3
"फैंसी फिटिंग जो एक ट्रांसफॉर्म_इफ एल्गोरिथ्म में है?" यही कारण है कि है एक "transform_if एल्गोरिथ्म" को छोड़कर यह सब कुछ hardcoded गया है।
आर। मार्टिनो फर्नांडीस

2
यह एक ट्रांसफॉर्म_इफ़ के बराबर प्रदर्शन करता है। बस यह कि एल्गोरिदम आपके कोड को सरल बनाने या किसी तरह से इसे बेहतर बनाने के लिए माना जाता है, इसे और अधिक जटिल नहीं बनाते हैं।
कैशबैक

5

इतने लंबे समय के बाद इस सवाल को पुनर्जीवित करने के लिए क्षमा करें। मुझे हाल ही में इसी तरह की आवश्यकता थी। मैंने इसे back_insert_iterator का एक संस्करण लिखकर हल किया है जो एक बढ़ावा देता है :: वैकल्पिक:

template<class Container>
struct optional_back_insert_iterator
: public std::iterator< std::output_iterator_tag,
void, void, void, void >
{
    explicit optional_back_insert_iterator( Container& c )
    : container(std::addressof(c))
    {}

    using value_type = typename Container::value_type;

    optional_back_insert_iterator<Container>&
    operator=( const boost::optional<value_type> opt )
    {
        if (opt) {
            container->push_back(std::move(opt.value()));
        }
        return *this;
    }

    optional_back_insert_iterator<Container>&
    operator*() {
        return *this;
    }

    optional_back_insert_iterator<Container>&
    operator++() {
        return *this;
    }

    optional_back_insert_iterator<Container>&
    operator++(int) {
        return *this;
    }

protected:
    Container* container;
};

template<class Container>
optional_back_insert_iterator<Container> optional_back_inserter(Container& container)
{
    return optional_back_insert_iterator<Container>(container);
}

इस तरह इस्तेमाल किया:

transform(begin(s), end(s),
          optional_back_inserter(d),
          [](const auto& s) -> boost::optional<size_t> {
              if (s.length() > 1)
                  return { s.length() * 2 };
              else
                  return { boost::none };
          });

1
मापा नहीं जाता है - जब तक कि उपयोगकर्ता शिकायत नहीं करते हैं कि उनका अनुभव सीपीयू-बाउंड है (अर्थात कभी नहीं) मैं नैनोसेकंड की तुलना में शुद्धता से अधिक चिंतित हूं। हालाँकि मैं इसे गरीब होते हुए नहीं देख सकता। विकल्प बहुत सस्ते होते हैं क्योंकि कोई मेमोरी आवंटन नहीं होता है और टी कंस्ट्रक्टर को केवल तभी कहा जाता है यदि वैकल्पिक वास्तव में आबादी है। मुझे आशा है कि आशावादी सभी मृत कोड को समाप्त कर देंगे क्योंकि सभी कोड पथ संकलन के समय दिखाई दे रहे हैं।
रिचर्ड होजेस

हाँ। मैं सहमत हूँ अगर यह एक सामान्य उद्देश्य एल्गोरिथ्म (वास्तव में, उन के अंदर सामान्य निर्माण ब्लॉक) के बारे में नहीं थे। यह वह जगह है जहां मैं आमतौर पर उत्साहित नहीं होता जब तक कि कुछ उतना ही सरल न हो जितना इसे मिलता है। इसके अलावा, मैं किसी भी आउटपुट आईटीआर पर डेकोरेटर होने के लिए वैकल्पिक हैंडलिंग के लिए प्यार करता हूं (इसलिए कम से कम हमें आउटपुट पुनरावृत्तियों की रचनाशीलता मिलती है, जबकि हम एल्गोरिदम की रचनाशीलता की कमी को प्लग करने की कोशिश कर रहे हैं)।
सेह

तार्किक रूप से कोई अंतर नहीं है कि क्या आप पुनरावृत्ति के माध्यम से या ट्रांसफ़ॉर्म फ़ंक्शन में डेकोरेटर के माध्यम से वैकल्पिक इंसर्ट संभालते हैं। यह अंततः केवल एक झंडे का परीक्षण है। मुझे लगता है कि आप पाएंगे कि अनुकूलित कोड उसी तरह होगा। पूर्ण अनुकूलन के रास्ते में एकमात्र चीज अपवाद से निपटने के लिए होगी। Noexcept constructors होने के कारण T को चिह्नित करने से यह ठीक हो जाएगा।
रिचर्ड होजेस

आप किस रूप में कॉल को बदलना () लेना चाहेंगे? मुझे यकीन है कि हम एक कंपोजिट इटरेटर सुइट बना सकते हैं।
रिचर्ड होजेस

मुझे भी :) मैं आपके सुझाव पर टिप्पणी कर रहा था। मैं कुछ और प्रस्तावित नहीं कर रहा था (मेरे पास बहुत समय पहले था। चलो रेंज और
कंपोजेबल

3

मानक को इस तरह से डिज़ाइन किया गया है ताकि दोहराव को कम किया जा सके।

इस विशेष मामले में आप साधारण रेंज के लिए लूप के साथ अधिक पठनीय और रसीले तरीके से अल्गोरिटम के उद्देश्यों को प्राप्त कर सकते हैं।

// another way

vector<ha*> newVec;
for(auto& item : v) {
    if (item.i < 2) {
        newVec.push_back(&item);
    }
}

मैंने उदाहरण को संशोधित किया है ताकि यह संकलित हो, कुछ डायग्नोस्टिक्स को जोड़ा और ओपी के एल्गोरिथ्म और मेरा दोनों पक्ष को प्रस्तुत किया।

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

struct ha { 
    explicit ha(int a) : i(a) {}
    int i;   // added this to solve compile error
};

// added diagnostic helpers
ostream& operator<<(ostream& os, const ha& t) {
    os << "{ " << t.i << " }";
    return os;
}

ostream& operator<<(ostream& os, const ha* t) {
    os << "&" << *t;
    return os;
}

int main() 
{
    vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector
    // GOAL : make a vector of pointers to elements with i < 2
    vector<ha*> ph; // target vector
    vector<ha*> pv; // temporary vector
    // 1. 
    transform(v.begin(), v.end(), back_inserter(pv), 
        [](ha &arg) { return &arg; }); 
    // 2. 
    copy_if(pv.begin(), pv.end(), back_inserter(ph),
        [](ha *parg) { return parg->i < 2;  }); // 2. 

    // output diagnostics
    copy(begin(v), end(v), ostream_iterator<ha>(cout));
    cout << endl;
    copy(begin(ph), end(ph), ostream_iterator<ha*>(cout));
    cout << endl;


    // another way

    vector<ha*> newVec;
    for(auto& item : v) {
        if (item.i < 2) {
            newVec.push_back(&item);
        }
    }

    // diagnostics
    copy(begin(newVec), end(newVec), ostream_iterator<ha*>(cout));
    cout << endl;
    return 0;
}

3

बस कुछ समय बाद फिर से इस सवाल का पता लगाने के बाद, और संभावित उपयोगी जेनेरिक इटरेटर एडाप्टर्स की एक पूरी श्रृंखला को तैयार करने के बाद मुझे एहसास हुआ कि मूल प्रश्न से अधिक कुछ भी नहीं चाहिए std::reference_wrapper

एक सूचक के बजाय इसका उपयोग करें, और आप अच्छे हैं:

Live On Coliru

#include <algorithm>
#include <functional> // std::reference_wrapper
#include <iostream>
#include <vector>

struct ha {
    int i;
};

int main() {
    std::vector<ha> v { {1}, {7}, {1}, };

    std::vector<std::reference_wrapper<ha const> > ph; // target vector
    copy_if(v.begin(), v.end(), back_inserter(ph), [](const ha &parg) { return parg.i < 2; });

    for (ha const& el : ph)
        std::cout << el.i << " ";
}

प्रिंटों

1 1 

1

आप के copy_ifसाथ उपयोग कर सकते हैं । क्यों नहीं? परिभाषित करें OutputIt( प्रतिलिपि देखें ):

struct my_inserter: back_insert_iterator<vector<ha *>>
{
  my_inserter(vector<ha *> &dst)
    : back_insert_iterator<vector<ha *>>(back_inserter<vector<ha *>>(dst))
  {
  }
  my_inserter &operator *()
  {
    return *this;
  }
  my_inserter &operator =(ha &arg)
  {
    *static_cast< back_insert_iterator<vector<ha *>> &>(*this) = &arg;
    return *this;
  }
};

और अपना कोड फिर से लिखें:

int main() 
{
    vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector
    // GOAL : make a vector of pointers to elements with i < 2
    vector<ha*> ph; // target vector

    my_inserter yes(ph);
    copy_if(v.begin(), v.end(), yes,
        [](const ha &parg) { return parg.i < 2;  });

    return 0;
}

4
"क्यों नहीं?" - क्योंकि कोड मनुष्यों के लिए है। मेरे लिए घर्षण वास्तव में लंबोदर के बजाय फ़ंक्शन ऑब्जेक्ट लिखने के लिए वापस जाने से भी बदतर है। *static_cast< back_insert_iterator<vector<ha *>> &>(*this) = &arg;दोनों अपठनीय और अनावश्यक रूप से ठोस है। इस c ++ 17 को अधिक सामान्य उपयोगों के साथ देखें ।
sehe

यहां एक संस्करण बेस-इटरेटर को हार्डकोड नहीं करता है (इसलिए आप इसे std::insert_iterator<>या std::ostream_iterator<>उदाहरण के लिए उपयोग कर सकते हैं ) और यह भी कि आप एक परिवर्तन (जैसे कि लंबोदर) की आपूर्ति करते हैं। c ++ 17, c ++ 11 में
sehe

ध्यान दें, इस बिंदु पर, आधार-पुनरावृत्तियों को रखने के लिए बहुत कम कारण है, और आप बस: किसी भी फ़ंक्शन का उपयोग कर सकते हैं , ध्यान दें कि बूस्ट में बेहतर कार्यान्वयन होता है: बढ़ावा :: function_output_iterator । अब जो कुछ बचा है वह फिर से आविष्कार कर रहा है for_each_if:)
sehe

असल में, मूल प्रश्न को फिर से पढ़ना, आइए कारण की एक आवाज जोड़ते हैं - सिर्फ c ++ 11 मानक पुस्तकालय का उपयोग करते हुए।
सेह

0
template <class InputIt, class OutputIt, class BinaryOp>
OutputIt
transform_if(InputIt it, InputIt end, OutputIt oit, BinaryOp op)
{
    for(; it != end; ++it, (void) ++oit)
        op(oit, *it);
    return oit;
}

उपयोग: (ध्यान दें कि धारणा और परिवर्तन मैक्रोज़ नहीं हैं, वे जो भी स्थिति और परिवर्तन आप लागू करना चाहते हैं, उसके लिए प्लेसहोल्डर हैं)

std::vector a{1, 2, 3, 4};
std::vector b;

return transform_if(a.begin(), a.end(), b.begin(),
    [](auto oit, auto item)             // Note the use of 'auto' to make life easier
    {
        if(CONDITION(item))             // Here's the 'if' part
            *oit++ = TRANSFORM(item);   // Here's the 'transform' part
    }
);

क्या आप इस कार्यान्वयन उत्पादन को तैयार करेंगे? क्या यह गैर-प्रतिलिपि योग्य तत्वों के साथ अच्छी तरह से काम करेगा? या चाल चलने वाले?
sehe

0

यह केवल प्रश्न 1 का उत्तर है "क्या उपलब्ध C ++ मानक पुस्तकालय उपकरण के साथ अधिक सुरुचिपूर्ण वर्कअराउंड है?"।

यदि आप c ++ 17 का उपयोग कर सकते हैं तो आप std::optionalकेवल C ++ मानक पुस्तकालय कार्यक्षमता का उपयोग करके एक सरल समाधान के लिए उपयोग कर सकते हैं । std::nulloptमैपिंग नहीं होने की स्थिति में विचार करना है :

कोलिरु पर लाइव देखें

#include <iostream>
#include <optional>
#include <vector>

template <
    class InputIterator, class OutputIterator, 
    class UnaryOperator
>
OutputIterator filter_transform(InputIterator first1, InputIterator last1,
                            OutputIterator result, UnaryOperator op)
{
    while (first1 != last1) 
    {
        if (auto mapped = op(*first1)) {
            *result = std::move(mapped.value());
            ++result;
        }
        ++first1;
    }
    return result;
}

struct ha { 
    int i;
    explicit ha(int a) : i(a) {}
};

int main()
{
    std::vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector

    // GOAL : make a vector of pointers to elements with i < 2
    std::vector<ha*> ph; // target vector
    filter_transform(v.begin(), v.end(), back_inserter(ph), 
        [](ha &arg) { return arg.i < 2 ? std::make_optional(&arg) : std::nullopt; });

    for (auto p : ph)
        std::cout << p->i << std::endl;

    return 0;
}

ध्यान दें कि मैंने अभी C ++ में Rust के दृष्टिकोण को लागू किया है।


0

आप std::accumulateगंतव्य कंटेनर के लिए एक पॉइंटर पर संचालित होने वाले उपयोग कर सकते हैं :

Live On Coliru

#include <numeric>
#include <iostream>
#include <vector>

struct ha
{
    int i;
};

// filter and transform is here
std::vector<int> * fx(std::vector<int> *a, struct ha const & v)
{
    if (v.i < 2)
    {
        a->push_back(v.i);
    }

    return a;
}

int main()
{
    std::vector<ha> v { {1}, {7}, {1}, };

    std::vector<int> ph; // target vector

    std::accumulate(v.begin(), v.end(), &ph, fx);
    
    for (int el : ph)
    {
        std::cout << el << " ";
    }
}

प्रिंटों

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