Mudah untuk memulai.
Perpustakaan header tunggal.
API bebas makro.
Tidak ada alokasi memori heap.
Portabilitas: Terus diuji di bawah Ubuntu, MacOS dan Windows menggunakan GCC/Clang/MSVC.
Tidak ada dependensi eksternal.
Keandalan: Opsi Pemeriksaan Kompiler yang Ketat + Sanitizer + Valgrind.
Pola yang dapat dikomposisi.
Diperluas, pengguna dapat menentukan pola mereka sendiri, baik melalui menyusun yang ada, atau membuat yang baru.
Dukung wadah seperti tuple dan seperti rentang.
Dukungan parsial untuk ekspresi konstan.
matchit.h Cukup unduh file header matchit.h dan masukkan ke dalam direktori Anda untuk dependensi.
Itu saja.
Anda dapat mengunduh melalui perintah bash ini
wget https://raw.githubusercontent.com/BowenFu/matchit.cpp/main/include/matchit.hSertakan cuplikan kode di cmakelists Anda. TXT:
include (FetchContent)
FetchContent_Declare(
matchit
GIT_REPOSITORY https://github.com/BowenFu/matchit.cpp.git
GIT_TAG main)
FetchContent_GetProperties(matchit)
if ( NOT matchit_POPULATED)
FetchContent_Populate(matchit)
add_subdirectory ( ${matchit_SOURCE_DIR} ${matchit_BINARY_DIR}
EXCLUDE_FROM_ALL )
endif ()
message ( STATUS "Matchit header are present at ${matchit_SOURCE_DIR} " ) Dan tambahkan ${matchit_SOURCE_DIR}/include ke jalur sertakan Anda.
Ganti main dengan tag rilis terbaru untuk menghindari pemecahan kompatibilitas API.
Kloning repo via
git clone --depth 1 https://github.com/BowenFu/matchit.cpp
Instal perpustakaan melalui
cd matchit.cpp
cmake -B ./build
cd build
make install
Kemudian gunakan find_package di cmakelists.txt Anda.
(Terima kasih kepada @daljit97 untuk menambahkan dukungan.)
vcpkg install matchit
Sekarang perpustakaan telah dikirimkan ke Conan Center Index.
Anda sekarang dapat menginstal perpustakaan melalui Conan.
(Terima kasih kepada @SanBlch untuk menambahkan dukungan.)
Untuk membuat debug Anda lebih mudah, cobalah untuk menulis bodi fungsi lambda Anda dalam baris terpisah sehingga Anda dapat menetapkan titik istirahat di dalamnya.
pattern | xyz = [&]
{
// Separate lines for function body <- set break points here
}jauh lebih ramah debugging dibandingkan dengan
pattern | xyz = [&] { /* some codes here */ }, // <- Set break points here, you will debug into the library.Jangan men -debug ke perpustakaan ini kecuali Anda benar -benar memutuskan untuk membasmi / memperbaiki beberapa bug di perpustakaan ini, sama seperti Anda tidak akan men -debug ke varian STL atau rentang.
Silakan coba membuat sampel minimal untuk mereproduksi masalah yang telah Anda temui. Anda dapat melakukan root menyebabkan masalah lebih cepat dengan cara itu.
Anda juga dapat membuat masalah dalam repo ini dan melampirkan kode sampel minimal dan saya akan mencoba menanggapi sesegera mungkin (kadang -kadang harapkan penundaan satu atau dua hari).
Untuk detail desain sintaks, silakan merujuk ke referensi.
Dokumen dari Rust ke Cocok (IT) memberikan sampel yang setara untuk sampel karat yang sesuai.
Di sana Anda mungkin memiliki gambaran seperti apa pengkodean dengan match(it) nantinya.
Dokumen dari proposal pencocokan pola untuk mencocokkan (IT) memberikan sampel yang setara untuk sampel yang sesuai dalam proposal pencocokan pola pencocokan.
Di sana Anda akan melihat pro dan kontra perpustakaan atas proposal.
Mari kita mulai perjalanan di perpustakaan!
(Untuk sampel lengkap, silakan merujuk ke Direktori Sampel.)
Sampel berikut ini menunjukkan cara menerapkan Factororial menggunakan perpustakaan match(it) .
# include " matchit.h "
constexpr int32_t factorial ( int32_t n)
{
using namespace matchit ;
assert (n >= 0 );
return match (n)(
pattern | 0 = 1 ,
pattern | _ = [n] { return n * factorial (n - 1 ); }
);
}Sintaks dasar untuk pencocokan pola adalah
match (VALUE)
(
pattern | PATTERN1 = HANDLER1,
pattern | PATTERN2 = HANDLER2,
...
)Ini adalah panggilan fungsi dan akan mengembalikan nilai yang dikembalikan oleh penangan. Jenis pengembalian adalah tipe umum untuk semua penangan. Jenis pengembalian akan batal jika semua penangan tidak mengembalikan nilai. Jenis pengembalian yang tidak kompatibel dari beberapa penangan adalah kesalahan kompilasi. Ketika penangan mengembalikan nilai, polanya harus lengkap. Kesalahan runtime akan terjadi jika semua pola tidak cocok. Ini bukan kesalahan jika tipe pengembalian penangan semuanya batal.
Handler juga bisa berupa nilai atau variabel ID. 1 setara dengan []{return 1;} .
Wildcard _ akan cocok dengan nilai apa pun. Ini adalah praktik umum untuk selalu menggunakannya sebagai pola terakhir, memainkan peran yang sama di perpustakaan kami seperti default case untuk pernyataan switch , untuk menghindari melarikan diri dari kasus.
Kami dapat mencocokkan beberapa nilai secara bersamaan:
# include " matchit.h "
constexpr int32_t gcd ( int32_t a, int32_t b)
{
using namespace matchit ;
return match (a, b)(
pattern | ds (_, 0 ) = [&] { return a >= 0 ? a : -a; },
pattern | _ = [&] { return gcd (b, a%b); }
);
}
static_assert (gcd( 12 , 6 ) == 6); Perhatikan bahwa beberapa pola mendukung Constexpr Match, yaitu Anda dapat mencocokkannya pada waktu kompilasi. Dari cuplikan kode di atas, kita dapat melihat bahwa gcd(12, 6) dapat dieksekusi dalam waktu kompilasi.
Berbeda dari pola pencocokan dalam bahasa pemrograman lain, variabel dapat digunakan secara normal di dalam pola dalam match(it) , ini ditunjukkan pada sampel berikut:
# include " matchit.h "
# include < map >
template < typename Map, typename Key>
constexpr bool contains (Map const & map, Key const & key)
{
using namespace matchit ;
return match (map. find (key))(
pattern | map. end () = false ,
pattern | _ = true
);
}Kita dapat menggunakan pola predikat untuk menempatkan beberapa batasan pada nilai yang akan dicocokkan.
constexpr double relu ( double value)
{
return match (value)(
pattern | (_ >= 0 ) = value,
pattern | _ = 0
);
}
static_assert (relu( 5 ) == 5);
static_assert (relu(- 5 ) == 0); Kami membebani beberapa operator untuk simbol wildcard _ untuk memfasilitasi penggunaan predikat dasar.
Terkadang kami ingin berbagi satu penangan untuk berbagai pola, atau pola adalah penyelamatan:
# include " matchit.h "
constexpr bool isValid ( int32_t n)
{
using namespace matchit ;
return match (n)(
pattern | or_ ( 1 , 3 , 5 ) = true ,
pattern | _ = false
);
}
static_assert (isValid( 5 ));
static_assert (!isValid( 6 ));Dan pola untuk menggabungkan beberapa pola predikat.
Pola aplikasi sangat kuat ketika Anda ingin mengekstrak beberapa informasi dari subjek. Sintaksnya adalah
app (PROJECTION, PATTERN)Sampel sederhana untuk memeriksa apakah num besar bisa:
# include " matchit.h "
constexpr bool isLarge ( double value)
{
using namespace matchit ;
return match (value)(
pattern | app (_ * _, _ > 1000 ) = true ,
pattern | _ = false
);
}
// app with projection returning scalar types is supported by constexpr match.
static_assert (isLarge( 100 )); Perhatikan bahwa _ * _ menghasilkan objek fungsi yang menghitung kuadrat input, dapat dianggap sebagai versi pendek [](auto&& x){ return x*x;} .
Bisakah kita mengikat nilainya jika kita sudah mengekstraknya? Tentu, pola pengidentifikasi untuk Anda.
Mari kita mencatat hasil persegi, dengan pola pengidentifikasi kode akan
# include < iostream >
# include " matchit.h "
bool checkAndlogLarge ( double value)
{
using namespace matchit ;
Id< double > s;
return match (value)(
pattern | app (_ * _, s. at (_ > 1000 )) = [&] {
std::cout << value << " ^2 = " << *s << " > 1000! " << std::endl;
return true ; },
pattern | _ = false
);
} Untuk menggunakan pola pengidentifikasi, kita perlu mendefinisikan/mendeklarasikan pengidentifikasi ( Id<double> s ) terlebih dahulu . (Jangan menandai sebagai const.) Ini bisa sedikit aneh jika Anda menggunakan pola pengidentifikasi dalam bahasa pemrograman lainnya. Ini karena pembatasan bahasa. Tapi jangan kesal. Verbositas yang ditambahkan ini memungkinkan bagi kita untuk menggunakan variabel di dalam pola . Anda mungkin tidak pernah dapat melakukan ini dalam bahasa pemrograman lainnya.
Di sini * Operator digunakan untuk dereferensi nilai di dalam pengidentifikasi. Satu hal yang perlu diperhatikan adalah bahwa pengidentifikasi hanya valid di dalam lingkup match . Jangan mencoba untuk mendereferensi di luar.
Id::at mirip dengan @ pola dalam karat, yaitu, ikat nilainya ketika subpattern cocok.
Juga perhatikan ketika pengidentifikasi yang sama diikat beberapa kali, nilai terikat harus sama satu sama lain melalui operator== . Sampel untuk memeriksa apakah array simetris:
# include " matchit.h "
constexpr bool symmetric (std::array< int32_t , 5 > const & arr)
{
using namespace matchit ;
Id< int32_t > i, j;
return match (arr)(
pattern | ds (i, j, _, j, i) = true ,
pattern | _ = false
);
}
static_assert (symmetric(std::array< int32_t , 5 >{ 5 , 0 , 3 , 7 , 10 }) == false);
static_assert (symmetric(std::array< int32_t , 5 >{ 5 , 0 , 3 , 0 , 5 }) == true);
static_assert (symmetric(std::array< int32_t , 5 >{ 5 , 1 , 3 , 0 , 5 }) == false); Sekarang kita sampai pada bagian yang paling kuat: pola penghancuran . Pola penghancuran dapat digunakan untuk std::tuple , std::pair , std::array (wadah berukuran tetap), dan wadah dinamis atau rentang berukuran ( std::vector , std::list , std::set , dan sebagainya) dengan std::begin dan std::end dukungan.
Pola ds luar terluar dapat dihilangkan. Ketika pola menerima banyak parameter, mereka diperlakukan sebagai subpattern dari pola DS.
# include " matchit.h "
template < typename T1, typename T2>
constexpr auto eval (std::tuple< char , T1, T2> const & expr)
{
using namespace matchit ;
Id<T1> i;
Id<T2> j;
return match (expr)(
pattern | ds ( ' + ' , i, j) = i + j,
pattern | ds ( ' - ' , i, j) = i - j,
pattern | ds ( ' * ' , i, j) = i * j,
pattern | ds ( ' / ' , i, j) = i / j,
pattern | _ = []
{
assert ( false );
return - 1 ;
});
} Beberapa operator telah kelebihan beban untuk Id , jadi i + j akan mengembalikan fungsi nullary yang mengembalikan nilai *i + *j .
Ada juga cara untuk merestrukturisasi struct / kelas Anda, membuat pola aplikasi struct / class tuple Anda atau mengadopsi. Opsi kedua terlihat seperti
// Another option to destructure your struct / class.
constexpr auto dsByMember (DummyStruct const &v)
{
using namespace matchit ;
// compose patterns for destructuring struct DummyStruct.
constexpr auto dsA = dsVia (&DummyStruct::size, &DummyStruct::name);
Id< char const *> name;
return match (v)(
pattern | dsA ( 2 , name) = name,
pattern | _ = " not matched "
);
};
static_assert (dsByMember(DummyStruct{ 1 , " 123 " }) == std::string_view{ " not matched " });
static_assert (dsByMember(DummyStruct{ 2 , " 123 " }) == std::string_view{ " 123 " });Mari kita lanjutkan perjalanan. Terkadang Anda memiliki banyak pengidentifikasi dan Anda ingin memberikan batasan pada hubungan mereka. Apakah itu mungkin? Tentu! Di sinilah penjaga pertandingan . Sintaksnya adalah
pattern | PATTERN | when(GUARD) = HANDLERKatakanlah, kami hanya ingin mencocokkan ketika jumlah dua pengidentifikasi sama dengan beberapa nilai, kami dapat menulis kode sebagai
# include < array >
# include " matchit.h "
constexpr bool sumIs (std::array< int32_t , 2 > const & arr, int32_t s)
{
using namespace matchit ;
Id< int32_t > i, j;
return match (arr)(
pattern | ds (i, j) | when (i + j == s) = true ,
pattern | _ = false
);
}
static_assert (sumIs(std::array< int32_t , 2 >{ 5 , 6 }, 11 )); Itu keren, bukan? Perhatikan bahwa i + j == s akan mengembalikan fungsi pembatalan yang mengembalikan hasil *i + *j == s .
Sekarang kita sampai pada pola OOO . Apa itu? Anda mungkin bertanya. Dalam beberapa bahasa pemrograman itu disebut pola istirahat . Anda dapat mencocokkan jumlah item yang sewenang -wenang dengannya. Ini hanya dapat digunakan di dalam pola ds dan paling banyak satu pola OOO dapat muncul di dalam pola ds . Anda dapat menulis kode sebagai berikut saat Anda ingin memeriksa pola tuple.
# include < array >
# include " matchit.h "
template < typename Tuple>
constexpr int32_t detectTuplePattern (Tuple const & tuple)
{
using namespace matchit ;
return match (tuple)
(
pattern | ds ( 2 , ooo, 2 ) = 4 ,
pattern | ds ( 2 , ooo ) = 3 ,
pattern | ds (ooo, 2 ) = 2 ,
pattern | ds (ooo ) = 1
);
}
static_assert (detectTuplePattern(std::make_tuple( 2 , 3 , 5 , 7 , 2 )) == 4); Terlebih lagi, kita dapat mengikat subrange dengan pola ooo ketika merusak std::array atau wadah / rentang lainnya. Itu cukup keren. Kita dapat memeriksa apakah array/vector/list/set/map/subrange/... simetris dengan:
template < typename Range>
constexpr bool recursiveSymmetric (Range const &range)
{
Id< int32_t > i;
Id<SubrangeT<Range const >> subrange;
return match (range)(
pattern | ds (i, subrange. at (ooo), i) = [&] { return recursiveSymmetric (*subrange); },
pattern | ds (_, ooo, _) = false ,
pattern | _ = true
);Dalam pola pertama, kami mengharuskan kepala sama dengan akhir. Dan jika itu masalahnya, kami lebih lanjut memeriksa bagian istirahat (terikat ke subrange) melalui panggilan rekursif. Setelah beberapa panggilan bersarang gagal untuk memenuhi persyaratan itu (jatuh ke pola kedua), pemeriksaan gagal. Kalau tidak, ketika hanya ada satu elemen yang tersisa atau ukuran kisaran adalah nol, pola terakhir cocok, kami mengembalikan true.
Kami telah melakukannya dengan pola inti kami. Sekarang mari kita mulai perjalanan menyusun pola .
Anda harus terbiasa dengan beberapa pola dan tidak ada pola jika Anda telah menggunakan fitur pencocokan pola dalam karat.
Beberapa / tidak ada pola yang dapat digunakan untuk mencocokkan pointer mentah, std::optional , std::unique_ptr , std::shared_ptr dan tipe lain yang dapat dikonversi menjadi bool dan derereference. Sampel yang khas bisa
# include " matchit.h "
template < typename T>
constexpr auto square (std::optional<T> const & t)
{
using namespace matchit ;
Id<T> id;
return match (t)(
pattern | some (id) = id * id,
pattern | none = 0
);
}
constexpr auto x = std::make_optional( 5 );
static_assert (square(x) == 25);Beberapa pola menerima subpattern. Dalam sampel subpattern adalah pengidentifikasi dan kami mengikat hasil yang diserefferensi. Tidak ada pola yang sendirian.
Beberapa dan tidak ada pola yang bukan pola atom dalam match(it) , mereka disusun melalui
template < typename T>
constexpr auto cast = []( auto && input) {
return static_cast <T>(input);
};
constexpr auto deref = []( auto &&x) { return *x; };
constexpr auto some = []( auto const pat) {
return and_ ( app (cast< bool >, true ), app (deref, pat));
};
constexpr auto none = app(cast< bool >, false );Untuk beberapa pola, pertama -tama kita memberikan nilai ke nilai boolean, jika nilai boolean benar, kita dapat melakukan dereferensi lebih lanjut. Kalau tidak, pertandingan gagal. Untuk tidak ada pola kami cukup memeriksa apakah nilai boolean yang dikonversi salah.
Karena pola sangat berguna untuk menangani sum type , termasuk hierarki kelas, std::variant , dan std::any . std::variant dan std::any dapat dikunjungi sebagai
# include " matchit.h "
template < typename T>
constexpr auto getClassName (T const & v)
{
using namespace matchit ;
return match (v)(
pattern | as< char const *>(_) = " chars " ,
pattern | as< int32_t >(_) = " int32_t "
);
}
constexpr std::variant< int32_t , char const *> v = 123 ;
static_assert (getClassName(v) == std::string_view{ " int32_t " });Hirarki kelas dapat dicocokkan sebagai
struct Shape
{
virtual ~Shape () = default ;
};
struct Circle : Shape {};
struct Square : Shape {};
auto getClassName (Shape const &s)
{
return match (s)(
pattern | as<Circle>(_) = " Circle " ,
pattern | as<Square>(_) = " Square "
);
}Karena pola bukanlah pola atom. Itu disusun melalui
template < typename T>
constexpr AsPointer<T> asPointer;
template < typename T>
constexpr auto as = []( auto const pat) {
return app (asPointer<T>, some (pat));
}; Untuk kelas, dynamic_cast digunakan secara default untuk sebagai pola, tetapi kita dapat mengubah perilaku melalui titik kustomisasi . Pengguna dapat menyesuaikan casting turun melalui mendefinisikan fungsi get_if untuk kelas mereka, mirip dengan std::get_if untuk std::variant :
# include < iostream >
# include " matchit.h "
enum class Kind { kONE , kTWO };
class Num
{
public:
virtual ~Num () = default ;
virtual Kind kind () const = 0;
};
class One : public Num
{
public:
constexpr static auto k = Kind:: kONE ;
Kind kind () const override { return k; }
};
class Two : public Num
{
public:
constexpr static auto k = Kind:: kTWO ;
Kind kind () const override
{
return k;
}
};
template <Kind k>
constexpr auto kind = app(&Num::kind, k);
template < typename T>
auto get_if (Num const * num) {
return static_cast <T const *>(num-> kind () == T::k ? num : nullptr );
}
int32_t staticCastAs (Num const & input)
{
using namespace matchit ;
return match (input)(
pattern | as<One>(_) = 1 ,
pattern | kind<Kind:: kTWO > = 2 ,
pattern | _ = 3
);
}
int32_t main ()
{
std::cout << staticCastAs (One{}) << std::endl;
return 0 ;
}Ada titik kustomziasi tambahan.
Pengguna dapat mengkhususkan PatternTraits jika mereka ingin menambahkan pola baru.
Satu hal yang perlu diperhatikan adalah bahwa Id bukan tipe biasa. Salinan apa pun hanyalah referensi untuk itu. Jadi jangan mencoba mengembalikannya dari tempat didefinisikan.
Kasus yang buruk akan terjadi
auto badId ()
{
Id< int > x;
return x;
} Mengembalikan pola yang disusun termasuk Id lokal juga salah.
auto badPattern ()
{
Id< int > x;
return composeSomePattern (x);
} Praktik yang baik adalah mendefinisikan Id yang dekat dengan penggunaannya dalam pencocokan pola.
auto goodPattern ()
{
Id< int > x;
auto somePattern = composeSomePattern (x);
return match (...)
(
pattern | somePattern = ...
);
} mathiu adalah sistem aljabar komputer sederhana yang dibangun berdasarkan match(it) .
Sampel Sederhana mathiu :
auto const x = symbol( " x " );
auto const e = x ^ fraction( 2 , 3 );
auto const d = diff(e, x);
// prints (* 2/3 (^ x -1/3))
std::cout << toString(d) << std::endl;OpenNask: Assembler 80x86 seperti MASM/NASM untuk OS kecil.
Jika Anda mengetahui proyek lain menggunakan perpustakaan ini, beri tahu saya dengan mengirimkan masalah atau PR.
Jika Anda memiliki pertanyaan atau ide tentang perpustakaan, silakan buka masalah.
Diskusi / masalah / PRS dipersilakan.
Desain Sintaks / Pola match(it) telah sangat dipengaruhi oleh pekerjaan terkait ini
Jika Anda tertarik untuk match(it) , Anda mungkin juga tertarik pada HSPP yang membawa pemrograman gaya Haskell ke C ++.
Silakan bintangi repo, bagikan repo, atau sponsor satu dolar untuk memberi tahu saya masalah perpustakaan ini.
Terima kasih untuk semua untuk kode yang berkontribusi dan mengirim bug.
Secara khusus, berkat kontributor berikut:
Hugo Etchegoyen (@hugoetchegoyen)
Terima kasih kepada @e-Dant untuk mensponsori proyek ini.