मैं C ++ में टाइप सूचियों का कार्टासियन उत्पाद कैसे बना सकता हूं?


26

स्वयं व्याख्यात्मक।

मूल रूप से, कहो तो मेरे पास टाइप सूचियाँ हैं:

using type_list_1 = type_list<int, somestructA>;
using type_list_2 = type_list<somestructB>;
using type_list_3 = type_list<double, short>;

वे प्रकार सूचियों की चर संख्या हो सकती है।

मैं कार्टेसियन उत्पाद का टाइपलिस्ट कैसे प्राप्त करूं?

result = type_list<
type_list<int, somestructB, double>,
type_list<int, somestructB, short>,
type_list<somestructA, somestructB, double>,
type_list<somestructA, somestructB, short>
>;

मैंने दो-तरफा कार्टेसियन उत्पाद बनाने के तरीके के बारे में बताया है: यहां एक प्रकार की सूची का कार्टेशियन उत्पाद कैसे बनाया जाता है? , लेकिन n तरीका इतना तुच्छ नहीं लगता है।

अभी के लिए मैं कोशिश कर रहा हूँ ...

template <typename...> struct type_list{};

// To concatenate
template <typename... Ts, typename... Us>
constexpr auto operator|(type_list<Ts...>, type_list<Us...>) {
   return type_list{Ts{}..., Us{}...};
}

template <typename T, typename... Ts, typename... Us>
constexpr auto cross_product_two(type_list<T, Ts...>, type_list<Us...>) {
    return (type_list<type_list<T,Us>...>{} | ... | type_list<type_list<Ts, Us>...>{});
}

template <typename T, typename U, typename... Ts>
constexpr auto cross_product_impl() {
    if constexpr(sizeof...(Ts) >0) {
        return cross_product_impl<decltype(cross_product_two(T{}, U{})), Ts...>();
    } else {
        return cross_product_two(T{}, U{});
    }
}

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


8
उफ, आप सजा के लिए एक ग्लूटन हैं R
लाइट

मैं थोड़े इसे चूसता हूं, लेकिन क्या आप 2-वे कार्टेसियन उत्पाद को इस तरह से संशोधित कर सकते हैं: 1) पहला टाइपलिस्ट वास्तव में 1 प्रकार के टाइपिस्ट का टाइपिस्ट है; 2) टाइपिस्टों से दो प्रकार के कॉन्टेक्ट करने के बजाय, मेटाफ़ंक्शन दूसरी सूची के प्रकारों को "टाइपिस्ट" (पहले एक कार्टेसियन-उत्पाद-तरीके से) की "बच्चे" सूची में जोड़ देगा? यदि यह संभव है, तो समस्या को आसानी से पुनरावर्ती एल्गोरिथ्म के साथ हल किया जा सकता है।
स्मित्सिन

1
एक पुनरावर्ती कार्यान्वयन में वास्तविक कठिनाई यह है कि cartesian_productटाइप सूचियों की एक सूची है, और प्रत्येक पुनरावर्तन कदम पर आप प्रत्येक आंतरिक प्रकार की सूची में सामान जोड़ना चाहते हैं। पैक के उस दूसरे पैकिंग स्तर में आने से कुछ कटौती होती है ...
मैक्स लैंगहॉफ

1
मुझे लगता है कि आप इसे "रैखिक रूप से" एक एन-आयामी "टाइप स्पेस" के रूप में भी देख सकते हैं, जहां आप प्रत्येक "टाइप ग्रिड पॉइंट" को पार करना चाहते हैं। आप ग्रिड बिंदुओं की संख्या की गणना करते हैं, फिर आप इसे ऐसे ही पार करते हैं, जैसे आप एक चपटा एनडी सरणी के माध्यम से और प्रत्येक ग्रिड बिंदु पर प्रकारों की गणना करते हैं। विचार करने के लिए कुछ ...
मैक्स लैंगहॉफ

1
@MaxLanghof " C ++ 17 में टुपल्स का कार्टेशियन उत्पाद " की तर्ज पर कुछ ?
Deduplicator

जवाबों:


14

Boost.Mp11 के साथ , यह एक छोटा लाइनर है (हमेशा की तरह):

using result = mp_product<
    type_list,
    type_list_1, type_list_2, type_list_3>;

डेमो


1
पवित्र गाय ... लेकिन मैं यह इंगित करने के लिए बाध्य हूं कि (प्रत्येक कोड को गॉडबोल्ट पर कई बार नमूना करना) Mp11 संस्करण को संकलन करने में लगभग दो बार लगता है। यह सुनिश्चित नहीं है कि कितना ओवरहेड खुद को बढ़ावा देने वाले हेडर को पार्स कर रहा है और कितना तात्कालिक टेम्पलेट्स है ...
मैक्स लैंगहॉफ

1
@MaxLanghof ज़रूर। 1.5x यदि आप केवल algorithm.hppMp11 के बजाय सभी को शामिल करते हैं। और तब भी हम ०.०s एस बनाम ०.१२ के बीच बात कर रहे हैं। मुझे यह लिखने में कितना समय लगा, यह भी बताना होगा।
बैरी

8
@ बियर: एक सॉफ्टवेयर इंजीनियरिंग दृष्टिकोण से, आपके साथ 100%। वहाँ भी कितना आसान है यह एक हाथ से लुढ़का दृष्टिकोण बनाम पढ़ने के लिए है। इसके अलावा पुस्तकालय समाधान की शुद्धता सुनिश्चित करने के लिए आवश्यक कोई परीक्षण नहीं है। कुल मिलाकर कम कोड और उच्च आत्मविश्वास इसके जीवनकाल के लिए कम रखरखाव लागत को जन्म देगा।
एंडीज

मैं मानता हूं कि यह काफी सरल है, लेकिन दुर्भाग्य से ऐसी टीमें मौजूद हैं जो बढ़ावा देने के लिए तैयार हैं।
थीमैग्यानिक

ऐसी टीमें हैं जो हर चीज पर फख्र करती हैं। यह इसका उपयोग नहीं करने का एक कारण नहीं है।
तोमाज़ कैनबरा

13

ठीक मिल गया। यह सुंदर नहीं है, लेकिन यह काम करता है:

template<class ... T>
struct type_list{};

struct somestructA{};
struct somestructB{};

using type_list_1 = type_list<int, somestructA, char>;
using type_list_2 = type_list<somestructB>;
using type_list_3 = type_list<double, short, float>;

template<class TL1, class TL2>
struct add;

template<class ... T1s, class ... T2s>
struct add<type_list<T1s...>, type_list<T2s...>>
{
    using type = type_list<T1s..., T2s...>;
};

template<class ... TL>
struct concat;

template<class TL, class ... TLs>
struct concat<TL, TLs...>
{
    using type = typename add<TL, typename concat<TLs...>::type>::type;
};

template<class TL>
struct concat<TL>
{
    using type = TL;
};

static_assert(std::is_same_v<type_list<int, somestructA, char, double, short, float>, typename add<type_list_1, type_list_3>::type>);

template<class TL1, class TL2>
struct multiply_one;

// Prepends each element of T1 to the list T2.
template<class ... T1s, class ... T2s>
struct multiply_one<type_list<T1s...>, type_list<T2s...>>
{
    using type = typename concat<type_list<type_list<T1s, T2s...>...>>::type;
};

static_assert(std::is_same_v<
    type_list<
        type_list<int, double, short, float>,
        type_list<somestructA, double, short, float>,
        type_list<char, double, short, float>
        >,
    typename multiply_one<type_list_1, type_list_3>::type>);

// Prepends each element of TL to all type lists in TLL.
template<class TL, class TLL>
struct multiply_all;

template<class TL, class ... TLs>
struct multiply_all<TL, type_list<TLs...>>
{
    using type = typename concat<typename multiply_one<TL, TLs>::type...>::type;
};

static_assert(std::is_same_v<
    type_list<
        type_list<int, double, short, float>,
        type_list<somestructA, double, short, float>,
        type_list<char, double, short, float>
        >,
    typename multiply_all<type_list_1, type_list<type_list_3>>::type>);

static_assert(std::is_same_v<
    type_list<
        type_list<int, somestructB>,
        type_list<somestructA, somestructB>,
        type_list<char, somestructB>,
        type_list<int, double, short, float>,
        type_list<somestructA, double, short, float>,
        type_list<char, double, short, float>
        >,
    typename multiply_all<type_list_1, type_list<type_list_2, type_list_3>>::type>);

template<class TL, class ... TLs>
struct cartesian_product
{
    using type = typename multiply_all<TL, typename cartesian_product<TLs...>::type>::type;
};

template<class ... Ts>
struct cartesian_product<type_list<Ts...>>
{
    using type = type_list<type_list<Ts>...>;
};


using expected_result = type_list<
    type_list<int, somestructB, double>,
    type_list<somestructA, somestructB, double>,
    type_list<char, somestructB, double>,
    type_list<int, somestructB, short>,
    type_list<somestructA, somestructB, short>,
    type_list<char, somestructB, short>,
    type_list<int, somestructB, float>,
    type_list<somestructA, somestructB, float>,
    type_list<char, somestructB, float>
>;

static_assert(std::is_same_v<expected_result,
    cartesian_product<type_list_1, type_list_2, type_list_3>::type>);

https://godbolt.org/z/L5eamT

मैंने अपना static_assertटेस्ट वहां के लिए छोड़ दिया ... खैर, मुझे उम्मीद है कि वे मदद करेंगे।

इसके अलावा, मुझे यकीन है कि एक अच्छा समाधान होना चाहिए। लेकिन यह स्पष्ट था "मुझे पता है कि यह अंततः लक्ष्य तक ले जाएगा" पथ। मुझे अंततः एक concatया प्रकार को जोड़ने का सहारा लेना पड़ा , मुझे यकीन है कि अधिकांश क्रॉफ्ट को छोड़ने के लिए इसका उपयोग बहुत पहले किया जा सकता था।


4
टेम्प्लेट प्रोग्रामिंग जो मैं अनुसरण कर सकता हूं। वह तो कमाल है। मैंने आज कुछ सीखा।
जेरी यिर्मयाह

add में दो type_lists लगते हैं। आप कॉनसैट में जोड़ने के लिए कई प्रकार की सूचियों को कैसे पारित कर रहे हैं?
थीमैग्यानिक

@themagicalyang अच्छी तरह से देखा जाता है, यह एक बग है (जो परीक्षणों में नहीं लगा कि सभी शामिल सूचियां केवल लंबाई 2 थीं)। ...पुनरावर्ती अंदर जाना पड़ता है concatकॉल, नहीं बाहर। उत्तर (परीक्षण मामलों सहित) सही। शुद्धता अपेक्षाओं के संबंध में बैरी को सही साबित करता है :)
मैक्स लैंगहोफ़

क्या कार्टेशियन उत्पाद कॉल को आधारभूत रूप से एक से अधिक मल्टीपल_ऑल नहीं करना है?
थीमैजिआंग

@themagicalyang No. cartesian_productपुनरावृत्ति लागू करता है। multiply_allकरता है amultiply_oneTLsपैक में प्रत्येक प्रकार की सूची के लिए । cartesian_product::typeएक प्रकार की सूची है। multiply_allएक प्रकार की सूची और एक प्रकार की सूची लेता है। multiply_oneदो प्रकार सूचियों लेता है a1, a2, a3और b1, b2, b3और बनाता है a1, b1, b2, b3, a2, b1, b2, b3, a3, b1, b2, b3। आपको कटौती के इन दो स्तरों ( multiply_all, multiply_one) की आवश्यकता है क्योंकि आपको "भिन्नता" के दो स्तरों से नीचे उतरने की आवश्यकता है, प्रश्न पर मेरी पहली टिप्पणी देखें।
मैक्स लैंगहॉफ

9

बचाव के लिए फिर से भावों को मोड़ो

template<typename... Ts>
typelist<typelist<Ts>...> layered(typelist<Ts...>);

template<typename... Ts, typename... Us>
auto operator+(typelist<Ts...>, typelist<Us...>)
    -> typelist<Ts..., Us...>;

template<typename T, typename... Us>
auto operator*(typelist<T>, typelist<Us...>)
    -> typelist<decltype(T{} + Us{})...>;

template<typename... Ts, typename TL>
auto operator^(typelist<Ts...>, TL tl)
    -> decltype(((typelist<Ts>{} * tl) + ...));

template<typename... TLs>
using product_t = decltype((layered(TLs{}) ^ ...));

और आपने कल लिया। यह O (1) तात्कालिकता गहराई होने की पुनरावृत्ति पर अतिरिक्त लाभ है।

struct A0;
struct A1;
struct B0;
struct B1;
struct C0;
struct C1;
struct C2;

using t1 = typelist<A0, A1>;
using t2 = typelist<B0, B1>;
using t3 = typelist<C0, C1, C2>; 

using p1 = product_t<t1, t2>;
using p2 = product_t<t1, t2, t3>;

using expect1 = typelist<typelist<A0, B0>,
                         typelist<A0, B1>,
                         typelist<A1, B0>,
                         typelist<A1, B1>>;

using expect2 = typelist<typelist<A0, B0, C0>,
                         typelist<A0, B0, C1>,
                         typelist<A0, B0, C2>,
                         typelist<A0, B1, C0>,
                         typelist<A0, B1, C1>,
                         typelist<A0, B1, C2>,
                         typelist<A1, B0, C0>,
                         typelist<A1, B0, C1>,
                         typelist<A1, B0, C2>,
                         typelist<A1, B1, C0>,
                         typelist<A1, B1, C1>,
                         typelist<A1, B1, C2>>;

static_assert(std::is_same_v<p1, expect1>);
static_assert(std::is_same_v<p2, expect2>);

यह मुझे धमकाता है। क्या टीएल 1 * टीएल 2 * टीएल 3 = क्रॉसपोर्स्क परिणाम के रूप में इसका प्रतिनिधित्व करने का एक तरीका है?
विषयगत

@themagicalyang "क्रॉस उत्पाद परिणाम" से आपका क्या अभिप्राय है?
राहगीर

मूल रूप से इसके बजाय using result = product_t<t1,t2,t3>... कुछ इस रूप में प्रतिनिधित्व करने के लिए using result = decltype(t1{} * t2{} * t3{});। हम्म, अब ठीक है कि यह इसके बारे में सोचता है, चूंकि decltypeअपरिहार्य है, बस उर्फ ​​का उपयोग करना जैसा आपने दिया है, अधिक सहज है।
विषयगत

दिलचस्प! ऑपरेटर ओवरलोडिंग का उपयोग करने से आपको मेरे द्वारा किए गए पुनरावृत्तियों के बजाय गुना अभिव्यक्ति मिलती है। इसके अलावा यह बहुत अधिक संक्षिप्त बनाता है। मैं इसे अगली बार के लिए ध्यान में रखूँगा!
मैक्स लैंगहॉफ

@PasserBy क्या उन सभी सहायक ऑपरेटरों और कार्यों को एक ही नामस्थान में होना चाहिए? मैं एक नाम स्थान के अंदर सब कुछ डालने और बाहर के नामस्थान से एक उपनाम का उपयोग करके product_t तक पहुंचने के साथ समस्याएँ प्राप्त कर रहा हूं।
थिमेजिकल वैंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.