C ++ 20'deki aralık aralığı: Kararların tasarımı

Adanali

Active member
C ++ 20'deki aralık aralığı: Kararların tasarımı


  1. C ++ 20'deki aralık aralığı: Kararların tasarımı

Ranges Kütüphanesi sayesinde, Standart Kütüphane (STL) ile çalışmak çok daha rahat ve verimli hale geldi. Her şeyden önce, aralıkların tembel aralığının algoritmaları doğrudan kap üzerinde çalışabilir ve bir araya getirilebilir. Ayrıca, Ranges kütüphanesi bilmeniz gereken bazı özel tasarım kararları aldı.










Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.













C ++ 20'deki aralık aralığına yaklaşmadan önce, bazı setlerdeki aralıkların en önemli üç özelliğini özetlemek istiyorum: rütbenin algoritmaları doğrudan kap üzerinde çalışabilir, gerekirse konularını değerlendirebilir ve oluşturulabilir.

Doğrudan kapta


Range Bookshop, gibi bir konteynere izin verir std::ranges::sort doğrudan kap üzerinde çalışabilir:



// sortRanges.cpp

#include <algorithm>&#13;
#include <iostream>&#13;
#include <vector>&#13;
&#13;
int main() {&#13;
&#13;
std::vector<int> myVec{-3, 5, 0, 7, -4};&#13;
std::ranges::sort(myVec); // (1)&#13;
for (auto v: myVec) std::cout << v << " "; // -4, -3, 0, 5, 7&#13;
&#13;
}


Aksine, klasik std::sort İki yineleyici tarafından tanımlanan bir alanda: std:sort(myVec.begin(), myVec.end()).



Aralıkların algoritmaları tembeldir ve bestelenebilir.

Tembel değerlendirme ve fonksiyonel kompozisyon


Aşağıdaki program primesLazy.cpp Her iki işlevselliği de kullanın. Bir milyondan başlayarak ilk on ana numarayı oluşturun.



// primesLazy.cpp&#13;
&#13;
#include <iostream>&#13;
#include <ranges>&#13;
&#13;
&#13;
bool isPrime(int i) {&#13;
for (int j=2; j*j <= i; ++j){&#13;
if (i % j == 0) return false;&#13;
}&#13;
return true;&#13;
}&#13;
&#13;
int main() {&#13;
&#13;
std::cout << 'n';&#13;
&#13;
auto odd = [](int i){ return i % 2 == 1; };&#13;
&#13;
for (int i: std::views::iota(1'000'000) | std::views::filter(odd) &#13;
| std::views::filter(isPrime) &#13;
| std::views::take(10)) {&#13;
std::cout << i << " "; &#13;
}&#13;
&#13;
std::cout << 'n';&#13;
&#13;
}


Fonksiyonel kompozisyon soldan sağa okunabilir: 1'000'000 başlangıç (std::views::iota(1'000'000)) ve iki filtre uygulayın. Her filtrenin bir yüklem ihtiyacı vardır. İlk filtre, garip öğelerin geçmesine izin verir (std::views::filter(odd)) ve ikinci filtre, asal sayıların geçmesine izin verirstd::views::filter(isPrime)). On sayıdan sonra (std::views::take(10)) Sonsuz veri akışını tutuyorum. Son olarak, bir milyonla başlayan ilk on ana sayıdır.









Bu, bu veri boru hattının işlenmesine başlayanların sorusunu gündeme getirir. Sağdan sola gidiyor. Desenke (std::views::take(10)) bir sonraki değere sahip olmak ve selefini istemek istiyor. Bu istek, aralık tabanlı döngü bir sonraki değeri sağlayana kadar devam edecektir. Döngü sonsuz bir veri akışı üretebilir, ancak sadece istek üzerine yapar. Bu tembel bir değerlendirme.

Bu benim kısa özetimdi. Nöbetçiler, projeksiyon ve kavramlar da dahil olmak üzere Ranges Kütüphanesi hakkında daha fazla bilgi edinmek istiyorsanız, önceki makalelerimi okuyun:

  1. Gamme Kütüphanesi
  2. Aralık aralığına sahip fonksiyonel modeller
  3. C ++ 20'deki aralık aralığı: Daha fazla ayrıntı
  4. Rütbe ile projeksiyonlar
  5. Sıralı Sentinler ve Kavramlar
  6. Rütbeli geliştirilmiş yineleyiciler
  7. Ranges Kütüphanesi ile Pythonic: Menzil ve Filtre
  8. Pythons aralığı işlevi, ikinci
  9. Python harita işlevi
Şimdi yeni bir şey hakkında yazmak istiyorum.

Tasarım Kararları


Verimlilik nedenlerinden dolayı, aralık kütüphanesi bazı benzersiz tasarım kararları aldı. Onları tanımak ve takip etmek önemlidir.

Kim begin-Üye işlevi std::ranges::filter_view Çalışılan, aşağıdakilere karşılık gelen bir kod bulur:



if constexpr (!ranges::forward_range<V>)&#13;
return /* iterator */{*this, ranges::find_if(base_, std::ref(*pred_))};&#13;
else&#13;
{&#13;
if (!begin_.has_value())&#13;
begin_ = ranges::find_if(base_, std::ref(*pred_)); // caching&#13;
return /* iterator */{*this, begin_.value())};&#13;
}


Yuvalama if-Acture bir analizi hak eder: Birincisi, derleyici kontrol ederse begin_.has_value() true VE. Aksi takdirde belirler begin_. Bu, bu üyenin içinde çalıştığı anlamına gelir. std::ranges::filter_view-Bjekt ara ürün daha sonra aradığınızda kullanmak için. Bu ara arşivin ilginç sonuçları var. Bunu bir kod keskin nişancı kullanarak göstermek istiyorum.



// cachingRanges.cpp&#13;
&#13;
#include <numeric>&#13;
#include <iostream>&#13;
#include <ranges>&#13;
#include <vector>&#13;
&#13;
int main() {&#13;
&#13;
std::vector<int> vec(1'000'000);&#13;
std::iota(vec.begin(), vec.end(), 0);&#13;
&#13;
for (int i: vec | std::views::filter([](auto v) { return v > 1000; }) &#13;
| std::views::take(5)) {&#13;
std::cout << i << " "; // 1001 1002 1003 1004 1005&#13;
}&#13;
&#13;
}


İlk çağrı std::views::filter([](auto v) { return v > 1000; }) Başlangıyı belirler ve aşağıdaki çağrılarda tekrar kullanır. Bu ara belleğin avantajı açıktır: boru hattının sonraki yinelemeleri önlenir. Ancak ciddi dezavantajlar da vardır: önbellek sorunları ve sürekli sorunlar.

Önbellek


Aralıklar için en önemli iki önbellek kuralı:

  • Görünümü değişen bir aralık için kullanmayın!
  • Bir görünümü kopyalamayın!
Önceki programın aşağıdaki değişikliği cachingRanges.cpp Her iki kuralı da kırıyor:



// cachingIssuesRanges.cpp&#13;
&#13;
#include <concepts>&#13;
#include <forward_list>&#13;
#include <iostream>&#13;
#include <numeric>&#13;
#include <ranges>&#13;
#include <vector>&#13;
&#13;
void printElements(std::ranges::input_range auto&& rang) {&#13;
for (int i: rang) {&#13;
std::cout << i << " "; &#13;
}&#13;
std::cout << 'n';&#13;
}&#13;
&#13;
int main() {&#13;
&#13;
std::cout << 'n';&#13;
&#13;
std::vector<int> vec{-3, 10, 4, -7, 9, 0, 5, -5}; // (1)&#13;
std::forward_list<int> forL{-3, 10, 4, -7, 9, 0, 5, -5}; // (2)&#13;
&#13;
auto first5Vector = vec | std::views::filter([](auto v) { return v > 0; }) // (3)&#13;
| std::views::take(5);&#13;
&#13;
auto first5ForList = forL | std::views::filter([](auto v) { return v > 0; }) // (4) &#13;
| std::views::take(5); &#13;
&#13;
printElements(first5Vector); // 10 4 9 5 // (5)&#13;
printElements(first5ForList); // 10 4 9 5 // (6)&#13;
&#13;
std::cout << 'n';&#13;
&#13;
vec.insert(vec.begin(), 10);&#13;
forL.insert_after(forL.before_begin(), 10);&#13;
&#13;
&#13;
printElements(first5Vector); // -3 10 4 9 5&#13;
printElements(first5ForList); // 10 4 9 5&#13;
&#13;
std::cout << 'n';&#13;
&#13;
auto first5VectorCopy{first5Vector}; // (7)&#13;
auto first5ForListCopy{first5ForList}; // (8)&#13;
&#13;
printElements(first5VectorCopy); // -3 10 4 9 5&#13;
printElements(first5ForListCopy); // 10 10 4 9 5&#13;
&#13;
std::cout << 'n';&#13;
&#13;
}&#13;



Sorunu daha iyi anlamak için çıktıyı doğrudan kaynak koduna yazdım. Program, aşağıdaki adımları bir std::vector ve bir std::forward_list Başından sonuna kadar. Her şeyden önce, her iki konteyner de başlatma listesindedir {-3, 10, 4, -7, 9, 0, 5, -5} Başlatılan (1) ve (2). Bu yüzden iki görüntü (3) ve (4) oluşturuyorum. İki görüntüleme first5Vector VE first5ForList 0'dan büyük olan ilk beş elementten oluşur. Karşılık gelen değerler (5) ve (6) 'da görüntülenir.

Şimdi ilk kuralı bozuyorum: “Alanları değiştirmek için görünümü kullanmayın”. Sayıyı iki konteynerin başlangıcına ekliyorum 10 A. Yani şovlar first5Vector . -3 onun first5ForList (7) ve (8) 'de ikinci kuralın kesintiye alınmasından sonra, “bir görünümü kopyalamayın” first5ForListCopy geçersiz. first5VectorCopy Hala yanlış sayıları gösterir. Sonunda, program burada.









İşte basit bir ampirik kural: Görünümleri tanımladıktan hemen sonra kullanın!

İşlev printElements İleri referans olarak da bilinen evrensel referans için konularınızı kabul edin.

Sırada ne var?


Bir sonraki makalemde yazıyorum çünkü bir işlev evrensel referans için herhangi bir vizyon almalıdır.

İşte sizin adınıza bir not


Ne yazık ki, size çok ciddi bir ilerici sinir hastalığım olduğunu söylemeliyim. Bu bloga ne kadar devam edebileceğimden emin değilim. Şu anda sadece eşimin yardımıyla eğitim veya konferanslara gidebilirim. Biz (ailem ve ben) bu zorlukla agresif bir şekilde yüzleşmeye karar verdik. Bu nedenle, hastalığımdaki sadık okuyucularımı sevmek benim için önemliydi.


(RME)
 
Üst