Bagaimana cara menyortir vektor yang berisi objek kustom (yaitu yang ditentukan pengguna).
Mungkin, algoritma STL standar menyortir bersama dengan predikat (fungsi atau objek fungsi) yang akan beroperasi pada salah satu bidang (sebagai kunci untuk menyortir) dalam objek kustom harus digunakan.
Apakah saya di jalur yang benar?
248
Jawaban:
Contoh sederhana menggunakan
std::sort
Sunting: Seperti yang ditunjukkan Kirill V. Lyadvinsky, alih-alih memberikan predikat semacam, Anda dapat menerapkan
operator<
untukMyStruct
:Menggunakan metode ini berarti Anda cukup mengurutkan vektor sebagai berikut:
Sunting2: Seperti yang disarankan Kappa, Anda juga dapat mengurutkan vektor dalam urutan menurun dengan membebani
>
operator dan mengubah sedikit panggilan:Dan Anda harus memanggil sortir sebagai:
sumber
std::sort(vec.begin(), vec.end(), greater<MyStruct>())
yang bersih dan elegan.#include <functional>
menggunakan "std :: yang lebih besar".operator<
dan menggunakan salah satustd::sort(vec.begin(), vec.end());
ataustd::sort(vec.rbegin(), vec.rend());
tergantung pada apakah Anda ingin memiliki pesanan naik atau turun.Untuk kepentingan liputan. Saya mengajukan implementasi menggunakan ekspresi lambda .
C ++ 11
C ++ 14
sumber
>
alih-alih<
untuk mendapatkan pesanan menurun.Anda bisa menggunakan functor sebagai argumen ketiga
std::sort
, atau Anda bisa mendefinisikanoperator<
di kelas Anda.sumber
const
di akhir tanda tangan fungsi?const
.const
kunci di akhir tanda tangan menentukan bahwaoperator()
fungsi tidak mengubah instance dariXgreater
struct (yang secara umum dapat memiliki variabel anggota), sedangkan menunjukkanconst
untuk nilai input hanya menentukan bahwa nilai input tersebut tidak dapat diubah.Menyortir
vector
berbagai objek jenis kustom yangX
dapat diterapkan (dapat dimutasikan input) jenis ini dapat dicapai dengan menggunakan berbagai metode, terutama termasuk penggunaan algoritma perpustakaan standar sepertisort
,stable_sort
,partial_sort
ataupartial_sort_copy
.Karena sebagian besar teknik, untuk mendapatkan urutan relatif
X
elemen, telah diposting, saya akan mulai dengan beberapa catatan tentang "mengapa" dan "kapan" untuk menggunakan berbagai pendekatan.Pendekatan "terbaik" akan tergantung pada berbagai faktor:
X
objek merupakan tugas yang umum atau jarang (akankah rentang tersebut diurutkan menjadi beberapa tempat yang berbeda dalam program atau oleh pengguna perpustakaan)?X
objek menjadi sangat mudah?Jika menyortir rentang
X
adalah tugas umum dan penyortiran yang dicapai diharapkan (yaituX
hanya membungkus nilai fundamental tunggal) maka mungkin akan pergi untuk kelebihanoperator<
karena memungkinkan menyortir tanpa fuzz (seperti melewati pembanding yang tepat dengan benar) dan berulang kali menghasilkan diharapkan hasil.Jika menyortir adalah tugas umum atau mungkin diperlukan dalam konteks yang berbeda, tetapi ada beberapa kriteria yang dapat digunakan untuk menyortir
X
objek, saya akan mencari Functors (operator()
fungsi kelas kustom yang berlebihan ) atau pointer fungsi (yaitu satu functor / fungsi untuk pemesanan leksikal dan satu lagi untuk pemesanan alami).Jika menyortir rentang tipe
X
jarang atau tidak mungkin dalam konteks lain saya cenderung menggunakan lambdas daripada mengacaukan namespace dengan lebih banyak fungsi atau tipe.Ini terutama benar jika penyortiran tidak "jelas" atau "alami" dalam beberapa cara. Anda dapat dengan mudah mendapatkan logika di balik pemesanan ketika melihat lambda yang diterapkan di tempat sedangkan
operator<
buram pada pandangan pertama dan Anda harus melihat definisi untuk tahu apa logika pemesanan akan diterapkan.Namun perlu dicatat, bahwa
operator<
definisi tunggal adalah titik kegagalan tunggal sedangkan banyak lambas adalah titik kegagalan ganda dan memerlukan perhatian lebih.Jika definisi
operator<
tidak tersedia di mana penyortiran dilakukan / template penyortiran dikompilasi, kompiler mungkin dipaksa untuk membuat panggilan fungsi ketika membandingkan objek, alih-alih menguraikan logika pemesanan yang mungkin merupakan kelemahan parah (setidaknya ketika optimisasi waktu taut / pembuatan kode tidak diterapkan).Cara untuk mencapai keterbandingan
class X
untuk menggunakan algoritma pengurutan perpustakaan standarBiarkan
std::vector<X> vec_X;
danstd::vector<Y> vec_Y;
1. Kelebihan
T::operator<(T)
atauoperator<(T, T)
dan gunakan templat pustaka standar yang tidak mengharapkan fungsi perbandingan.Salah satu anggota kelebihan beban
operator<
:atau gratis
operator<
:2. Gunakan pointer fungsi dengan fungsi perbandingan khusus sebagai parameter fungsi penyortiran.
3. Buat
bool operator()(T, T)
kelebihan untuk jenis kustom yang dapat dilewatkan sebagai fungsi perbandingan.Definisi objek fungsi tersebut dapat ditulis sedikit lebih umum menggunakan C ++ 11 dan templat:
yang dapat digunakan untuk mengurutkan jenis apa pun dengan anggota
i
pendukung<
.4. Lewati penutupan anonymus (lambda) sebagai parameter perbandingan ke fungsi penyortiran.
Di mana C ++ 14 memungkinkan ekspresi lambda yang lebih umum:
yang bisa dibungkus makro
membuat pembuatan pembanding biasa cukup lancar:
sumber
bool X_less(X const &l, X const &r) const { return l.i < r.i; }
untuk pembanding tetapiconst
kata kunci harus dihapus (karena itu bukan fungsi anggota).std::sort
atau serupa, tetapi membutuhkan contohCompare
, misalnya ketika instantiating astd::set
?template<class T, class C> std::set<T, C> make_set(C const& compare) { return std::set<T, C>{ compare }; }
yang bisa digunakan sepertiauto xset = make_set<X>([](auto && l, auto && r) { return l.i < r.i; });
.Anda berada di jalur yang benar.
std::sort
akan digunakanoperator<
sebagai fungsi perbandingan secara default. Jadi untuk mengurutkan objek Anda, Anda harus membebanibool operator<( const T&, const T& )
atau menyediakan functor yang melakukan perbandingan, seperti ini:Keuntungan penggunaan functor adalah Anda dapat menggunakan fungsi dengan akses ke anggota pribadi kelas.
sumber
operator<
anggota kelas (atau struct), karena yang global bisa menggunakan anggota yang dilindungi atau pribadi. Atau Anda harus membuat teman struct C.Saya ingin tahu apakah ada dampak yang dapat diukur pada kinerja antara berbagai cara yang dapat disebut std :: sort, jadi saya telah membuat tes sederhana ini:
Apa yang dilakukannya adalah menciptakan vektor acak, dan kemudian mengukur berapa banyak waktu yang diperlukan untuk menyalinnya dan mengurutkan salinannya (dan menghitung beberapa checksum untuk menghindari penghapusan kode mati yang terlalu kuat).
Saya mengkompilasi dengan g ++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)
Ini hasilnya:
Sepertinya semua opsi kecuali untuk melewatkan pointer fungsi sangat mirip, dan melewatkan pointer fungsi menyebabkan penalti + 30%.
Ini juga terlihat seperti operator <versi ~ 1% lebih lambat (saya mengulangi tes beberapa kali dan efeknya tetap ada), yang agak aneh karena menunjukkan bahwa kode yang dihasilkan berbeda (saya kurang memiliki keterampilan untuk menganalisis - menghemat- temps output).
sumber
Ya,
std::sort()
dengan parameter ketiga (fungsi atau objek) akan lebih mudah. Contoh: http://www.cplusplus.com/reference/algorithm/sort/sumber
Di kelas Anda, Anda mungkin membebani operator "<".
sumber
Di bawah ini adalah kode menggunakan lambdas
sumber
sumber
Anda dapat menggunakan kelas pembanding yang ditentukan pengguna.
sumber
Untuk mengurutkan vektor, Anda dapat menggunakan algoritma sort () di.
Parameter ketiga yang digunakan bisa lebih besar atau lebih kecil atau fungsi atau objek apa pun juga bisa digunakan. Namun operator default adalah <jika Anda membiarkan parameter ketiga kosong.
sumber
jika perbandingan salah, itu akan melakukan "swap".
sumber