membantu Anda menulis kode C++ yang ringkas dan mudah dibaca.
Kode yang bagus sebagian besar harus mendokumentasikan dirinya sendiri, tetapi saat menggunakan C++ pada kenyataannya, Anda dapat menghadapi hal-hal tingkat rendah seperti iterator atau loop tulisan tangan yang mengalihkan perhatian dari esensi sebenarnya dari kode Anda.
FunctionalPlus adalah pustaka kecil khusus header yang mendukung Anda dalam mengurangi gangguan kode dan menangani hanya satu tingkat abstraksi dalam satu waktu. Dengan meningkatkan keringkasan dan pemeliharaan kode Anda, hal ini dapat meningkatkan produktivitas (dan kesenangan!) dalam jangka panjang. Ini mencapai tujuan tersebut dengan menyediakan fungsi murni dan mudah digunakan yang membebaskan Anda dari penerapan aliran kontrol yang umum digunakan berulang kali.
Katakanlah Anda memiliki daftar angka dan hanya tertarik pada angka ganjil saja.
bool is_odd_int ( int x) { return x % 2 != 0 ; }
int main ()
{
typedef vector< int > Ints;
Ints values = { 24 , 11 , 65 , 44 , 80 , 18 , 73 , 90 , 69 , 18 };
// todo: get odd numbers from values ...
}
Ada berbagai kemungkinan untuk mencapai tujuan Anda. Beberapa di antaranya adalah:
Ints odds;
for ( int x : values)
{
if ( is_odd_int (x))
{
odds. push_back (x);
}
}
std::copy_if
dari STL Ints odds;
std::copy_if (std::begin(values), std::end(values),
std::back_inserter(odds), is_odd_int);
keep_if
dari FunctionalPlus
auto odds = fplus::keep_if(is_odd_int, values);
Jika menurut Anda versi 3 adalah versi yang paling menyenangkan untuk digunakan, Anda mungkin menyukai FunctionalPlus. Dan jika Anda masih menganggap perulangan for yang ditulis tangan lebih mudah dipahami, pertimbangkan juga apa yang akan terjadi jika isi perulangan (yaitu fungsi lambda yang sesuai dalam panggilan ke fplus::keep_if
) akan lebih panjang. Saat membaca keep_if
Anda akan langsung mengetahui bahwa odds
hanya dapat berisi elemen yang berasal dari values
dan dipilih oleh beberapa predikat, yang mungkin rumit. Dalam kasus perulangan for, Anda tidak akan tahu apa yang terjadi sampai Anda membaca keseluruhan isi perulangan. Versi loop mungkin memerlukan komentar di bagian atas yang menyatakan apa yang akan terlihat dari penggunaan keep_if
pada pandangan pertama.
Di bawah ini adalah beberapa contoh singkat yang menunjukkan hal-hal baik yang dapat Anda lakukan dengan fungsi dan container menggunakan FunctionalPlus.
Anda dapat menguji konten wadah untuk berbagai properti, misalnya
# include < fplus/fplus.hpp >
# include < iostream >
int main ()
{
std::list things = { " same old " , " same old " };
if ( fplus::all_the_same (things))
std::cout << " All things being equal. " << std::endl;
}
team
kamiAda juga beberapa fungsi praktis untuk mengambil properti container. Misalnya Anda dapat menghitung kemunculan karakter dalam sebuah string.
# include < fplus/fplus.hpp >
# include < iostream >
int main ()
{
std::string team = " Our team is great. I love everybody I work with. " ;
std::cout << " There actually are this many 'I's in team: " <<
fplus::count ( " I " , fplus::split_words ( false , team)) << std::endl;
}
Keluaran:
There actually are this many 'I's in team: 2
Menemukan elemen dengan nilai tertinggi dalam sebuah wadah sangat sederhana dibandingkan dengan versi tulisan tangan (1, 2).
# include < fplus/fplus.hpp >
# include < iostream >
struct cat
{
double cuteness () const
{
return softness_ * temperature_ * roundness_ * fur_amount_ - size_;
}
std::string name_;
double softness_;
double temperature_;
double size_;
double roundness_;
double fur_amount_;
};
void main ()
{
std::vector cats = {
{ " Tigger " , 5 , 5 , 5 , 5 , 5 },
{ " Simba " , 2 , 9 , 9 , 2 , 7 },
{ " Muffin " , 9 , 4 , 2 , 8 , 6 },
{ " Garfield " , 6 , 5 , 7 , 9 , 5 }};
auto cutest_cat = fplus::maximum_on ( std::mem_fn (&cat::cuteness), cats);
std::cout << cutest_cat. name_ <<
" is happy and sleepy. *purr* *purr* *purr* " << std::endl;
}
Keluaran:
Muffin is happy and sleepy. *purr* *purr* *purr*
Misalkan Anda diberikan fungsi berikut.
std::list< int > collatz_seq ( int x);
Dan Anda ingin membuat std::map
yang berisi representasi string dari urutan Collatz untuk semua angka di bawah 30. Anda juga dapat mengimplementasikannya dengan baik secara fungsional.
# include < fplus/fplus.hpp >
# include < iostream >
// std::list collatz_seq(std::uint64_t x) { ... }
int main ()
{
typedef std::list< int > Ints;
// [1, 2, 3 ... 29]
auto xs = fplus::numbers( 1 , 30 );
// A function that does [1, 2, 3, 4, 5] -> "[1 => 2 => 3 => 4 => 5]"
auto show_ints = fplus::bind_1st_of_2 (fplus::show_cont_with, " => " );
// A composed function that calculates a Collatz sequence and shows it.
auto show_collats_seq = fplus::compose (collatz_seq, show_ints);
// Associate the numbers with the string representation of their sequences.
auto collatz_dict = fplus::create_map_with (show_collats_seq, xs);
// Print some of the sequences.
std::cout << collatz_dict[ 13 ] << std::endl;
std::cout << collatz_dict[ 17 ] << std::endl;
}
Keluaran:
[13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1]
[17 => 52 => 26 => 13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1]
Fungsi yang ditampilkan tidak hanya bekerja dengan kontainer STL default seperti std::vector
, std::list
, std::deque
, std::string
dll. tetapi juga dengan kontainer khusus yang menyediakan antarmuka serupa.
FunctionalPlus menyimpulkan tipe untuk Anda jika memungkinkan. Mari kita ambil satu baris kode dari contoh Collatz:
auto show_collats_seq = fplus::compose(collatz_seq, show_ints);
collatz_seq
adalah fungsi yang mengambil uint64_t
dan mengembalikan list
. show_ints
mengambil list
dan mengembalikan string
. Dengan memanfaatkan function_traits
, yang ditulis oleh kennyim, ekspresi fplus::compose(collatz_seq, show_ints)
dapat secara otomatis disimpulkan sebagai fungsi yang menggunakan uint64_t
dan mengembalikan string
, sehingga Anda tidak perlu memberikan petunjuk tipe secara manual ke kompiler.
Jika dua fungsi yang "tipe penghubungnya" tidak cocok diteruskan, pesan kesalahan jelas yang menjelaskan masalah tersebut akan dihasilkan. FunctionalPlus menggunakan pernyataan waktu kompilasi untuk menghindari pesan kesalahan panjang yang membingungkan yang dihasilkan oleh kompiler ketika menghadapi kesalahan ketik dalam templat fungsi.
Mengubah cara Anda memprogram dari "menulis loop Anda sendiri dan if yang disarangkan" menjadi "menyusun dan menggunakan fungsi-fungsi kecil" akan menghasilkan lebih banyak kesalahan pada waktu kompilasi tetapi akan membuahkan hasil dengan memiliki lebih sedikit kesalahan pada waktu proses. Selain itu, kesalahan waktu kompilasi yang lebih tepat akan mengurangi waktu yang dihabiskan untuk debugging.
Artikel "Pemrograman fungsional dalam C++ dengan pustaka FunctionalPlus; hari ini: Batu Permata tantangan HackerRank" memberikan pengenalan yang lancar ke dalam pustaka dengan menunjukkan bagaimana seseorang dapat mengembangkan solusi elegan untuk suatu masalah menggunakan pendekatan FunctionalPlus.
Di Udemy juga terdapat kursus "Pemrograman Fungsional menggunakan C++" yang banyak menggunakan FunctionalPlus untuk menjelaskan konsep fungsional umum.
Tutorial "Batu Permata" di atas menjelaskan bagaimana seseorang dapat menerapkan pemikiran fungsional untuk sampai pada solusi di bawah ini untuk masalah berikut:
Temukan jumlah karakter yang ada di setiap baris teks masukan.
std::string gemstone_count ( const std::string& input)
{
using namespace fplus ;
typedef std::set characters;
const auto lines = split_lines ( false , input); // false = no empty lines
const auto sets = transform (
convert_container,
lines);
// Build the intersection of all given character sets (one per line).
const auto gem_elements = fold_left_1 (
set_intersection, sets);
return show ( size_of_cont (gem_elements));
}
Dengan menggunakan fungsionalitas dari namespace fwd
, Anda dapat melakukannya tanpa variabel sementara, dan memperjelas bahwa seluruh proses hanya mendorong masukan melalui rangkaian fungsi, mirip dengan konsep pipa di baris perintah Unix.
std::string gemstone_count_fwd_apply ( const std::string& input)
{
using namespace fplus ;
typedef std::set characters;
return fwd::apply (
input
, fwd::split_lines ( false )
, fwd::transform (convert_container)
, fwd::fold_left_1 (set_intersection)
, fwd::size_of_cont ()
, fwd::show ()
);
}
Dalam fplus::fwd::
Anda menemukan banyak fungsi fplus::
lagi, tetapi dalam versi kari sebagian, yaitu fplus::foo : (a, b, c) -> d
memiliki padanannya dengan fplus::foo : (a, b) -> (c -> d)
. Hal ini memungkinkan gaya di atas.
Sebagai alternatif terhadap versi aplikasi penerusan, Anda juga dapat menulis bebas titik dan menentukan fungsi Anda berdasarkan komposisi:
using namespace fplus ;
typedef std::set characters;
const auto gemstone_count_fwd_compose = fwd::compose(
fwd::split_lines ( false ),
fwd::transform(convert_container),
fwd::fold_left_1(set_intersection),
fwd::size_of_cont(),
fwd::show()
);
Omong-omong, jika Anda memerlukan parameter fungsi biner dalam urutan terbalik, namespace fplus::fwd::flip
juga ada. fplus::bar : (a, b) -> c
tidak hanya memiliki analognya di fplus::fwd::bar : a -> b -> c
tetapi juga di fplus::fwd::flip::bar : b -> a -> c
.
Jika Anda mencari fungsi FunctionalPlus tertentu yang belum Anda ketahui namanya, tentu saja Anda dapat menggunakan fitur pelengkapan otomatis IDE Anda untuk menelusuri konten namespace fplus
. Namun cara yang disarankan adalah dengan menggunakan website pencarian FunctionalPlus API . Anda dapat dengan cepat mencari berdasarkan kata kunci atau tanda tangan tipe fungsi dengannya. Jika mau, Anda juga dapat menelusuri kode sumber menggunakan Sourcegraph.
Fungsi dasarnya cepat, berkat konsep abstraksi C++ tanpa overhead. Berikut beberapa pengukuran dari contoh pertama, yang diambil pada PC desktop standar, dikompilasi dengan GCC dan flag O3
.
5000 random numbers, keep odd ones, 20000 consecutive runs accumulated
----------------------------------------------------------------------
| Hand-written for loop | std::copy_if | fplus::keep_if |
|-----------------------|--------------|----------------|
| 0.632 s | 0.641 s | 0.627 s |
Jadi kompiler tampaknya melakukan pekerjaan yang sangat baik dalam mengoptimalkan dan menyelaraskan semuanya agar pada dasarnya menyamai kode mesin dari segi kinerja.
Fungsi yang lebih kompleks terkadang dapat ditulis dengan cara yang lebih optimal. Jika Anda menggunakan FunctionalPlus dalam skenario kinerja kritis dan pembuatan profil menunjukkan bahwa Anda memerlukan versi fungsi yang lebih cepat, harap beri tahu saya atau bahkan bantu meningkatkan FunctionalPlus.
FunctionalPlus secara internal sering kali dapat beroperasi di tempat jika wadah tertentu memiliki nilai-r (misalnya dalam panggilan berantai) dan dengan demikian menghindari banyak alokasi dan penyalinan yang tidak perlu. Namun hal ini tidak berlaku di semua situasi. Namun, berkat bekerja dengan bahasa multi-paradigma, seseorang dapat dengan mudah menggabungkan kode imperatif yang dioptimalkan secara manual dengan fungsi fplus
. Untungnya, pengalaman (alias pembuatan profil) menunjukkan bahwa dalam banyak kasus, sebagian besar kode dalam suatu aplikasi tidak relevan dengan kinerja keseluruhan dan konsumsi memori. Jadi pada awalnya berfokus pada produktivitas pengembang dan keterbacaan kode adalah ide yang bagus.
FunctionalPlus dan range-v3 (dasar untuk ranges
di C++-20) memiliki kesamaan, seperti yang ditunjukkan cuplikan kode berikut.
const auto times_3 = []( int i){ return 3 * i;};
const auto is_odd_int = []( int i){ return i % 2 != 0 ;};
const auto as_string_length = []( int i){ return std::to_string (i). size ();};
// FunctionalPlus
using namespace fplus ;
const auto result_fplus = fwd::apply(
numbers ( 0 , 15000000 )
, fwd::transform(times_3)
, fwd::drop_if(is_odd_int)
, fwd::transform(as_string_length)
, fwd::sum());
// range-v3
const auto result_range_v3 =
accumulate (
views::ints ( 0 , ranges::unreachable)
| views::take( 15000000 )
| views::transform(times_3)
| views::remove_if(is_odd_int)
| views::transform(as_string_length), 0);
Namun ada beberapa perbedaan. Rentang Range-v3 bersifat lambat, yang berarti tidak ada memori perantara yang dialokasikan selama satu langkah rantai pemrosesan seperti di atas. Saat menggunakan FunctionalPlus di sisi lain Anda bekerja dengan wadah STL normal. Mengimplementasikan fungsi baru juga lebih sederhana dibandingkan dengan menulis adaptor rentang baru. Selain itu FunctionalPlus menyediakan lebih banyak fungsi dan memiliki situs web pencarian API. Jadi pilihan antara kedua perpustakaan tersebut bergantung pada preferensi Anda dan kebutuhan proyek.
Diperlukan kompiler yang kompatibel dengan C++14 . Kompiler dari versi ini baik-baik saja:
Panduan tentang berbagai cara menginstal FunctionalPlus dapat ditemukan di INSTALL.md.
Fungsionalitas di perpustakaan ini awalnya berkembang karena kebutuhan pribadi saya akan perpustakaan ini saat menggunakan C++ secara teratur. Saya mencoba yang terbaik untuk membuatnya bebas kesalahan dan senyaman mungkin untuk digunakan. API mungkin masih berubah di masa mendatang. Jika Anda memiliki saran, menemukan kesalahan, melewatkan beberapa fungsi, atau ingin memberikan masukan/kritik umum, saya ingin mendengar pendapat Anda. Tentu saja, kontribusinya juga sangat diharapkan.
Didistribusikan di bawah Lisensi Perangkat Lunak Boost, Versi 1.0. (Lihat file yang menyertai LICENSE
atau salin di http://www.boost.org/LICENSE_1_0.txt)