namespace cpp {}

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


kennen:include:ranges

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.


Vorhergehende Überarbeitung
kennen:include:ranges [2024-02-22 21:04] (aktuell) – [Beispiele] rrichter
Zeile 1: Zeile 1:
 +====== <ranges> ======
 +Bereiche sind Elementfolgen, auf die ''%%begin(r)%%'' und ''%%end(r)%%'' anwendbar sind.
 +Die Algorithmen der Standardbibliothek erlauben seit [[..:begriffe#C++20]] die Schreibweise ''std::ranges::sort(v)'' statt ''std::sort(begin(v), end(v))''.
 +Allerdings sind die einzelnen Algorithmen jeweils nacheinander auszuführen, also nicht koppelbar.
 +MIt einem anderen Ansatz soll dieser Nachteil behoben werden.
 +
 +Im Namensraum ''std::ranges::views'' oder kurz ''std::views'' 
 +definierte Sichten auf Bereiche lassen sich über Pipes ''|'' aneinander koppeln. 
 +Sie liefern die Werte eines Bereiches einzeln auf Anforderung, also über [[https://de.wikipedia.org/wiki/Lazy_Evaluation|lazy evaluation]].
 +
 +<code cpp views_pipelines.cpp>
 +#include <iostream>
 +#include <ranges>
 +
 +int main()
 +{
 +    using namespace std::views;
 +    
 +    auto output = 
 +          iota(1)
 +        | filter([](int x){ return x%2 == 0; })
 +        | transform([](int x){ return 3*x; })
 +        | take(10);
 +
 +    // no calculation is done up to here (lazy evaluation)
 +
 +    for (auto e : output) std::cout << e << ' ';
 +}
 +</code>
 +
 +Anfang und Ende eines Views müssen nicht denselben Typ besitzen. Werden Iteratoren des selben Typs benötigt, können diese mit ''std::views::common'' erzeugt werden:
 +
 +<code cpp views_common.cpp>
 +#include <iostream>
 +#include <ranges>
 +#include <vector>
 +
 +int main()
 +{
 +    using namespace std::views;
 +    
 +    auto numbers = iota(1, 10) | common;
 +    auto v = std::vector<int>{numbers.begin(), numbers.end()};
 +    
 +    for (auto e : v) std::cout << e << ' ';
 +}
 +</code>
 +===== Quellen und Senken =====
 +Sequentielle und assoziative (!) Container können als Quellbereiche dienen. Mit ''ranges::to<Container>()'' (C++23) wird ein neuer Container als Senke für die verarbeiteten Daten erzeugt. Der Elementtyp wird automatisch bestimmt.
 +
 +<code cpp to.cpp>
 +#include <iostream>
 +#include <ranges>
 +#include <set>
 +#include <vector>
 +
 +int main()
 +{
 +    std::set s{ 'h', 'a', 'l', 'o' };
 +    auto v = s | std::views::take(2) | std::ranges::to<std::vector>();
 +    for (auto e : v) std::cout << e; 
 +}
 +</code>
 +
 +===== Fabriken =====
 +erzeugen Bereiche (° : definiert in [[..:begriffe#C++20]], ³ : [[..:begriffe#C++23]]): 
 +
 +| ''empty'' | leerer Bereich | ° |
 +| ''single(value)''| Bereich mit nur einem Wert | ° | 
 +| ''iota(start)''| (unendlicher) Bereich mit Werten beginnend bei ''start'', zählt mit ''++'' hoch | ° | 
 +| ''iota(start, end)''| Zählbereich endet vor ''end'' | ° | 
 +| ''ranges::istream_view<T>(istream)'' | liest Werte vom Typ ''T'' aus dem Eingabestrom ''istream'' | ° |
 +| ''repeat(value)'' | wiederholt den Wert unbegrenzt oft | ³ |
 +| ''repeat(value, n)'' | liefert den Wert genau ''n'' Mal | ³ |
 +
 +<code cpp istream_view.cpp>
 +#include <iostream>
 +#include <ranges>
 +#include <sstream>
 +#include <string>
 +
 +int main()
 +{
 +    auto input = std::istringstream{"0 1 2 3 4 5 6 7 8 9"};
 +    auto below_5 = std::ranges::istream_view<int>(input) 
 +        | std::views::take_while([](auto x) { return x < 5; });
 +
 +    for (auto e : below_5) std::cout << e << ' ';
 +}
 +</code>
 +
 +===== Sichten =====
 +übernehmen einen oder mehrere Bereiche, verarbeiten dessen / deren Elemente ''e'' und geben sie weiter.
 +
 +| ''all'' | gibt alle Elemente weiter | ° |
 +| ''adjacent<N>'' | erzeugt Tupel aus ''N'' benachbarten Elementen  | ³ |
 +| ''adjacent_transform<N>(f)'' | wendet ''f(e1,e2,...)'' auf ''N'' benachbarte Elemente ''e1,e2,...'' an | ³ |
 +| ''as_const'' | Elemente dürfen nicht verändert werden | ³ |
 +| ''as_rvalue'' | erspart Kopieren: Elemente können per "move" übernommen werden | ³ |
 +| ''common'' | erzeugt Bereich, bei dem ''%%begin(r)%%'' und ''%%end(r)%%'' den selben Typ haben | ° |
 +| ''cartesian_product(rg1,rg2,...)'' | bildet die Kreuzmenge, d.h. alle Tupel aus jedem mit jedem aus 2 oder mehr gegebenen Bereichen | ³ |
 +| ''chunk(n)'' | unterteilt in Teilbereiche aus ''n'' aufeinander folgenden Elementen | ³ |
 +| ''chunk_by(pred)'' | aufeinander folgende Elemente ''a'',''b'' gehören zum gleichen Teilbereich, wenn ''pred(a,b)'' wahr ist | ³ |
 +| ''counted(iter, n)'' | liefert ''n'' Werte, beginnend mit Iterator ''iter'' | ° |
 +| ''drop(n)'' | überspringt die ersten ''n'' Elemente | ° |
 +| ''drop_while(pred)'' | überspringt Elemente, solange ''pred(e)'' erfüllt ist | ° |
 +| ''elements<N>'' | gibt den ''N''-ten Bestandteil von tupelartigen Elementen weiter | ° |
 +| ''enumerate'' | erzeugt Index-Wert-Paare | ³ |
 +| ''filter(pred)'' | gibt nur die Elemente weiter, für die ''pred(e)'' erfüllt ist | ° |
 +| ''join'' | wandelt einen Bereich von Bereichen in einen "flachen" Bereich um | ° |
 +| ''join_with(pattern)'' | verbindet Teilbereiche und fügt ''pattern'' dazwischen ein | ³ |
 +| ''keys'' | gibt alle Schlüssel von Schlüssel-Wert-Paaren weiter (''elements<0>'') | ° |
 +| ''pairwise'' | ''adjacent<2>'' | ³ |
 +| ''pairwise_transform(f)'' | ''adjacent_transform<2>(f)'' | ³ |
 +| ''reverse'' | kehrt Reihenfolge der Elemente um | ° |
 +| ''slide(n)'' | erzeugt einen gleitenden Fensterbereich aus n Elementen | ³ |
 +| ''stride(n)'' | springt immer n Elemente weiter | ³ |
 +| ''take(n)'' | gibt nur die ersten ''n'' Elemente weiter | ° |
 +| ''take_while(pred)'' | gibt Elemente nur solange weiter, wie ''pred(e)'' erfüllt ist | ° |
 +| ''transform(f)'' | wendet ''f'' auf jedes Element ''e'' an, gibt ''f(e)'' weiter  | ° |
 +| ''split(delim)'' | erzeugt einen Bereich von Teilbereichen, ''delim'' kann einzelnes Element oder eine Folge sein  | ° |
 +| ''values'' | gibt alle Werte von Schlüssel-Wert-Paaren weiter (''elements<1>'') | ° |
 +| ''zip(rg1,rg2,...)'' | erzeugt Tupel aus zwei oder mehr Bereichen | ³ |
 +| ''zip_transform(f,rg1,rg2,...)'' | ruft ''f(e1,e2,...)'' mit Argumenten ''e1,e2,...'' aus gegebenen Bereichen auf | ³ |
 +
 +
 +===== Beispiele =====
 +<file>
 +empty<T>                                 : []
 +single(42)                               : [42]
 +iota(0,6)                                : [0, 1, 2, 3, 4, 5]
 +iota(0) | take(3)                        : [0, 1, 2]
 +repeat(6) | take(3)                      : [6, 6, 6]
 +
 +counted(begin(v),size(v))                : [1, 3, 5, 4, 2]
 +v | all                                  : [1, 3, 5, 4, 2]
 +v | drop_while(below_5)                  : [5, 4, 2]
 +v | enumerate                            : [(0, 1), (1, 3), (2, 5), (3, 4), (4, 2)]
 +v | filter(below_5)                      : [1, 3, 4, 2]
 +v | reverse                              : [2, 4, 5, 3, 1]
 +v | take_while(below_5)                  : [1, 3]
 +v | transform(square)                    : [1, 9, 25, 16, 4]
 +
 +v | chunk(3)                             : [[1, 3, 5], [4, 2]]
 +v | chunk_by(std::ranges::less{})        : [[1, 3, 5], [4], [2]]
 +v | pairwise_transform(double_digit)     : [13, 35, 54, 42]
 +v | pairwise                             : [(1, 3), (3, 5), (5, 4), (4, 2)]
 +v | slide(2)                             : [[1, 3], [3, 5], [5, 4], [4, 2]]
 +v | stride(2)                            : [1, 5, 2]
 +
 +s                                        : ['A', 'B', ' ', 'C', 'D', 'E']
 +s | split(' '                          : [['A', 'B'], ['C', 'D', 'E']]
 +s | split(' ') | join                    : ['A', 'B', 'C', 'D', 'E']
 +s | split(' ') | join_with("<>"s)        : ['A', 'B', '<', '>', 'C', 'D', 'E']
 +
 +zip(v, s)                                : [(1, 'A'), (3, 'B'), (5, ' '), (4, 'C'), (2, 'D')]
 +zip_transform(double_digit, v, v)        : [11, 33, 55, 44, 22]
 +cartesian_product(v|take(2), s|take(2))  : [(1, 'A'), (1, 'B'), (3, 'A'), (3, 'B')]
 +
 +m                                        : {"answer": 42, "wrong": 54}
 +m | keys                                 : ["answer", "wrong"]
 +m | values                               : [42, 54]
 +</file>
 +ist die Ausgabe des Programms
 +<code cpp views_examples.cpp>
 +#include <ranges>
 +#include <map>
 +#include <string>
 +#include <vector>
 +
 +// waiting for C++23 support: P2286 Formatting Ranges ...
 +#if defined(__cpp_lib_format_ranges) && defined(__cpp_lib_print)
 +#include <print>
 +
 +template <typename Range, typename Msg>
 +void show(Range&& r, Msg message)
 +{
 +    std::print("{:<40}: {}", message, r);
 +}
 +
 +template <typename Range, typename Msg>
 +void show_range_of_ranges(Range&& r, Msg message)
 +{
 +    show(r, message);    
 +}
 +
 +template <typename Range, typename Msg>
 +void show_range_of_pairs(Range&& r, Msg message)
 +{
 +    show(r, message);    
 +}
 +
 +template <typename Range, typename Msg>
 +void show_map(Range&& r, Msg message)
 +{
 +    show(r, message);    
 +}
 +
 +#else
 +#include <iostream>
 +
 +auto decorate(auto e) 
 +{
 +    return e;
 +}
 +
 +auto decorate(char e) 
 +{
 +    return std::string("\'") + e + "\'";
 +}
 +
 +auto decorate(std::string e) 
 +{
 +    return std::string("\"") + e + "\"";
 +}
 +
 +template <typename Range, typename Msg>
 +void show(Range&& r, Msg message)
 +{
 +    std::cout.width(40);
 +    std::cout << std::left << message <<  " : [";
 +    int cnt = 0;    
 +    for (auto&& e : r) 
 +    {
 +        std::cout << (cnt++ ? ", ":"") << decorate(e);
 +    }    
 +    std::cout << "]\n";
 +}
 +
 +template <typename Range, typename Msg>
 +void show_range_of_ranges(Range&& r, Msg message)
 +{
 +    std::cout.width(40);
 +    std::cout << std::left << message << " : [";
 +    int cnt = 0;    
 +    for (auto&& inner_range : r) 
 +    {
 +        std::cout << (cnt++ ? ", ":"") << "[";
 +        int inner_cnt = 0;    
 +        for (auto&& e : inner_range) 
 +        {
 +            std::cout << (inner_cnt++ ? ", ":""
 +                      << decorate(e);
 +        }
 +        std::cout << "]";        
 +    }    
 +    std::cout << "]\n";
 +}
 +
 +template <typename Range, typename Msg>
 +void show_range_of_pairs(Range&& r, Msg message)
 +{
 +    std::cout.width(40);
 +    std::cout << std::left << message <<  " : [";
 +    int cnt = 0;    
 +    for (auto&& [a,b] : r) 
 +    {
 +        std::cout << (cnt++ ? ", ":""
 +                  << "(" << decorate(a) << ", " << decorate(b) << ")";    
 +    }    
 +    std::cout << "]\n";
 +}
 +
 +template <typename Range, typename Msg>
 +void show_map(Range&& r, Msg message)
 +{
 +    std::cout.width(40);
 +    std::cout << std::left << message <<  " : {";
 +    int cnt = 0;    
 +    for (auto&& [k,v] : r) 
 +    {
 +        std::cout << (cnt++ ? ", ":"")
 +                  << decorate(k) << ": " << decorate(v);    
 +    }    
 +    std::cout << "}\n";
 +}
 +
 +#endif // defined(__cpp_lib_format_ranges) && defined(__cpp_lib_print)
 +
 +int main()
 +{
 +    using namespace std::views;
 +    using namespace std::string_literals;
 +    
 +    auto v = std::vector{ 1, 3, 5, 4, 2 };
 +    auto s = "AB CDE"s;
 +    auto m = std::map<std::string,int>{{"wrong", 6*9}, {"answer", 42}};
 +    auto below_5 = [](int x){ return x < 5; };
 +    auto double_digit = [](int a, int b) { return 10*a + b; };
 +    
 +    show( empty<int>, "empty<T>");
 +    show( single(42), "single(42)");
 +    show( iota(0,6), "iota(0,6)");
 +    show( iota(0) | take(3), "iota(0) | take(3)");
 +    show( repeat(6) | take(3), "repeat(6) | take(3)");
 +    std::cout << '\n';
 +
 +    show( counted(begin(v), size(v)), "counted(begin(v),size(v))");
 +    show( v | all, "v | all");
 +    show( v | drop_while(below_5), "v | drop_while(below_5)");
 +    show_range_of_pairs(v | enumerate, "v | enumerate");
 +    show( v | filter(below_5), "v | filter(below_5)");
 +    show( v | reverse, "v | reverse");
 +    show( v | take_while(below_5), "v | take_while(below_5)");
 +    show( v | transform([](int x){ return x*x; }), "v | transform(square)");
 +    std::cout << '\n';
 +
 +    show_range_of_ranges( v | chunk(3), "v | chunk(3)" );
 +    show_range_of_ranges( v | chunk_by(std::ranges::less{}), "v | chunk_by(std::ranges::less{})" );
 +    show( v | pairwise_transform(double_digit), "v | pairwise_transform(double_digit)" );
 +    show_range_of_pairs( v | pairwise, "v | pairwise" );
 +    show_range_of_ranges( v | slide(2), "v | slide(2)" );
 +    show( v | stride(2), "v | stride(2)" );
 +    std::cout << '\n';
 +
 +    show( s , "s");
 +    show_range_of_ranges( s | split(' '), "s | split(\' \')");
 +    show( s | split(' ') | join, "s | split(\' \') | join");
 +    show( s | split(' ') | join_with("<>"s), "s | split(\' \') | join_with(\"<>\"s)");
 +    std::cout << '\n';
 +
 +    show_range_of_pairs( zip(v, s), "zip(v, s)" );
 +    show( zip_transform(double_digit, v, v), "zip_transform(double_digit, v, v)" );
 +    show_range_of_pairs( cartesian_product(v|take(2), s|take(2)), "cartesian_product(v|take(2), s|take(2))" );
 +    std::cout << '\n';
 +
 +    show_map( m , "m");
 +    show( m | keys , "m | keys");
 +    show( m | values , "m | values");
 +}
 +</code>
  

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki