C ++ 'da tip listelerinin Kartezyen ürününü nasıl oluşturabilirim?


26

Kendini açıklayıcı.

Temelde, şöyle listeleri var diyelim:

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

Değişken sayıda tip listesi olabilir.

Kartezyen bir ürün listesi nasıl alabilirim?

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

Burada verildiği gibi, iki yönlü bir Kartezyen ürününün nasıl oluşturulacağı konusunda bir fikrim var: Bir tür listesinin Kartezyen ürünü nasıl oluşturulur? , ama hiçbir şekilde önemsiz görünmüyor.

Şimdilik çalışıyorum ...

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{});
    }
}

Sadece doğru yapmanın ne kadar zor olduğunu göz önünde bulundurarak, Barry'nin cevabındaki gibi destek kullanın. Ne yazık ki, el haddelenmiş bir yaklaşımla sıkışmış olmalıyım çünkü takviye kullanmak ya da kullanmamak başka bir yerden gelen bir karardır :(


8
Oof, sen ceza için bir obursun 😏
Hafiflik Yarışları

Ben biraz emmek, ama 2 yönlü kartezyen ürün şu şekilde değiştirebilirsiniz: 1) ilk daktilo aslında 1 tip daktiloların bir tipik listesi; 2) tipel listelerden iki tür birleştirmek yerine, metafonksiyon ikinci listeden tipleri ilk tiplinin "alt" listelerine (kartezyen-ürün-yolunda) ekler mi? Mümkünse, problem özyinelemeli algoritma ile kolayca çözülebilir.
smitsyn

1
Özyinelemeli bir uygulamadaki gerçek zorluk cartesian_product, tür listelerinin bir listesidir ve her özyineleme adımında, her iç tür listesine bir şeyler eklemek istersiniz. Paketin ikinci ambalaj seviyesine girmek biraz kesinti gerektiriyor ...
Max Langhof

1
Sanırım bunu her "tip ızgara noktasını" çaprazlamak istediğiniz bir N-boyutlu "tip alanı" olarak bakarak "lineer" olarak da uygulayabilirsiniz. Izgara noktalarının sayısını hesaplarsınız, o zaman bunu düzleştirilmiş bir ND dizisi gibi geçirir ve her ızgara noktasındaki türleri hesaplarsınız. Dikkate alınması gereken bir şey ...
Max Langhof

1
@MaxLanghof " C ++ 17 tuples kartezyen bir ürün " hatları boyunca bir şey ?
Tekilleştirici

Yanıtlar:


14

İle Boost.Mp11 , bu kısa bir liner (her zaman olduğu gibi) 'dir:

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

Demo .


1
Kutsal inek ... Ama (her kodu godbolt üzerinde birkaç kez örnekleyerek) Mp11 versiyonunun derlenmesinin yaklaşık iki katı sürdüğünü belirtmek zorunda hissediyorum. Bu ek yükün ne kadarının destek başlığının kendisini ayrıştırdığından ve şablonların ne kadarının
ayrıldığından

1
@MaxLanghof Tabii. algorithm.hppTüm Mp11 yerine yalnızca 1.5x eklerseniz. Ve o zaman bile 0.08s ve 0.12s konuşuyoruz. Bunu da yazmamın ne kadar sürdüğünü hesaba katmak zorundayım.
Barry

8
@Barry:% 100 sizinle bir yazılım mühendisliği açısından. Bunun elle okunan bir yaklaşımla karşılaştırılması ne kadar kolay. Ayrıca kütüphane çözümünün doğruluğunu sağlamak için çok az veya hiç test gerekmez. Genel olarak daha az kod ve daha yüksek güven, kullanım ömrü boyunca daha düşük bakım maliyetlerine yol açacaktır.
AndyG

Bunun oldukça basit olduğuna katılıyorum, ancak maalesef takviye konusunda kaşlarını çatmış takımlar var.
themagicalyang

her şeye kaşlarını çatmış takımlar var. bu onu kullanmamak için bir sebep değildir.
Tomaz Canabrava

13

Tamam anladım. Güzel değil ama işe yarıyor:

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_assertOrada kendi testlerimi orada bıraktım ... Umarım yardımcı olurlar.

Ayrıca, daha iyi bir çözüm olması gerektiğine eminim. Ama bu "bunun sonunda hedefe yol açacağını biliyorum" yoluydu. Sonunda bir concatya da tür eklemek için başvurmak zorunda kaldım , eminim ki hamlelerin çoğunu atlamak için çok daha erken kullanılabilir.


4
Takip edebileceğim şablon programlama. Bu harika. Bugün bir şey öğrendim.
Jerry Jeremiah

add iki tip_listesi alır. Eş zamanlı olarak eklemek için birden çok tür listesinden nasıl geçiyorsunuz?
themagicalyang

@themagicalyang İyi tespit, bu bir hata (testlerin ilgili tüm listelerin sadece uzunluk 2 olduğu için bulamadığı). ...Özyinelemeli içine gitmek zorunda concatdeğil dışında, çağrı. Yanıt (test senaryoları dahil) düzeltildi. Barry'nin doğruluk beklentileri konusunda haklı olduğunu
kanıtladı

Kartezyen ürün multipl_all'ı bascially bir multip_one için çağırmıyor mu?
themagicalyang

@themagicalyang No. cartesian_productözyineleme uygular. , paketteki her tür listesi için multiply_alla yapar . tür listelerinin bir listesidir. bir tür listesi ve tür listelerinin bir listesini alır. İki tip liste alır ve ve yaratır , , . Bu iki kesinti düzeyine ( , ) ihtiyacınız var , çünkü iki "değişkenlik" düzeyine inmeniz gerekiyor, soru hakkındaki ilk yorumuma bakın. multiply_oneTLscartesian_product::typemultiply_allmultiply_onea1, a2, a3b1, b2, b3a1, b1, b2, b3a2, b1, b2, b3a3, b1, b2, b3multiply_allmultiply_one
Max Langhof

9

Kurtarmaya ifadeleri katlayın

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{}) ^ ...));

Ve işiniz bitti. Bu, O (1) örnekleme derinliğine sahip olmanın tekrarlanmasına göre ek bir yararı vardır.

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>);

Bu beni ilgilendiriyor. TL1 * TL2 * TL3 = çapraz nakil sonucu olarak göstermenin bir yolu var mı?
themagicalyang

@themagicalyang "Çapraz ürün sonucu" ile ne demek istiyorsun?
Passer

temel olarak using result = product_t<t1,t2,t3>... onu temsil etmenin bir yolu using result = decltype(t1{} * t2{} * t3{});. Hmm, şimdi bunu düşündüğü için, decltypekaçınılmaz olduğu için, sadece takma adı verdiğiniz gibi kullanmak daha sezgisel.
themagicalyang

İlginç! Operatör aşırı yüklemesini kullanmak, yapmam gereken özyineler yerine katlama ifadeleri verir. Ayrıca çok daha özlü hale getirir. Bir dahaki sefere aklımda tutacağım!
Max Langhof

@PasserBy Tüm bu yardımcı işleçlerin ve işlevlerin aynı ad alanında olması gerekiyor mu? Her şeyi bir ad alanı içine koyarak ve dış ad alanından bir takma ad kullanarak product_t erişme ile ilgili sorunlar alıyorum.
themagicalyang
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.