STL Algo für jeden zweiten Iterator?



  • Ich gewöhne mir gerade mal an, die STL Methoden zu benutzen und nicht immer alles selber zu schreiben.
    Ich hab ein double x und double y und einen vector<double> den ich loopen muss und abwechselnd mal x mal y addieren muss.
    Nun hab ich mir std::transform mal angeguckt, sehe jedoch nicht, wie ich diese alterierende verhalten damit erzeugen kann.

    Gibt es da was schnelles, um dies zu bewerkstelligen?



  • Seikilos schrieb:

    Ich hab ein double x und double y und einen vector<double> den ich loopen muss und abwechselnd mal x mal y addieren muss.

    Meinst du sowas?

    std::vector<double> vec = /* ... */;
    
    double x = vec[0] + vec[2] + vec[4] + /* ... */;
    double y = vec[1] + vec[3] + vec[5] + /* ... */;
    

    Falls ja, könntest du das folgendermassen machen. Den operator() kannst du natürlich auch übersichtlicher implementieren... 🤡

    struct xy_adder
    {
    	xy_adder(double& x, double& y) : x(x), y(y), is_x(false) {}
    
    	void operator() (double elem)
    	{
    		((is_x ^= 1) ? x : y) += elem;
    	}
    
    	double& x;
    	double& y;
    	bool is_x;
    };
    
    int main()
    {
    	std::vector<double> v;
    	// ...
    	double x = 0;
    	double y = 0;
    
    	std::for_each(v.begin(), v.end(), xy_adder(x,y));
    }
    

    Aber nicht immer ist ein separater Funktor die bessere Lösung. Manchmal reicht auch eine gewöhnliche Schleife.

    Edit: Werner Salomon hat eigentlich Recht, beherzige seinen Vorschlag. Vielleicht hilft dir mein Code ja sonst mal... 😉



  • .. klarer Fall von Designfehler. Du braucht eine Klasse Vektor(mathematisch) bzw. Position:

    struct Pos {
        double m_x;
        double m_y;
    };
    std::vector< Pos > ...;
    

    Ich bin sicher, dass das Dein Programm auch an anderen Stellen vereinfacht 😉
    Fertige Positions-Klassen dieser Art findest Du u.a. hier - musst halt immer den Z-Wert weglassen.



  • Willst du den vector<double> verändern oder die geänderten Werte in einen neuen vector packen? Falls ersteres müsste doch eigentlich for_each ausreichen.

    Davon unabhängig kannst du dir ja einen Funktor schreiben, der double x und double y übergeben bekommt und der intern eine Zählvariable hochzählt bei jedem Aufruf von op(). Dann kannst du bei gerader Zählvariable x addieren und bei ungerader Zählvariable y addieren.



  • Öh, ja, nein, ja 🙂

    Scherz. Also nochmal: Dieser Vektor ist linearisiert, wie ihr richtig erkannt habt. Das hat seinen Grund. Er wird so exakt als ein großer Speicherblock an die Grafikkarte geschickt.
    Wir verwenden osg::Vec2d und da wir nicht sicher sein können, ob x und y dort als einzige Zustände des Structs existieren (oder mal n byte alignment oder so), können wir auch nicht ohne umkopieren riskieren, diese blind zu linearisieren.

    Der code ist mit ner for loop simpel:

    for(unsigned i = 0; i < vertices.size()-1; i+=2)
       {
          vertices[i] = vertices[i]+_offset.x();
          vertices[i+1] = vertices[i+1]+_offset.y();
    
       }
    

    Ich dachte nur, es geht auch eleganter mit stl.


  • Mod

    Also ich hatte jetzt gerade mal einen eigenen Iterator implementiert:

    // transform algorithm example
    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    
    int op_increase (int i) { return ++i; }
    
    template<class T> class double_iterator
    {
    private:
      typename vector<T>::iterator it;
    public:
      double_iterator(typename vector<T>::iterator i):it(i){}
      double_iterator operator++()
      {
        ++it;
        ++it;
        return it;
      }
      double_iterator operator++(int)
      {
        typename vector<T>::iterator temp(it);
        ++it;
        ++it;
        return temp;
      }
      bool operator!=(const double_iterator<T>& rhs)
      {
        return it!=rhs.it;
      }
      bool operator==(const double_iterator<T>& rhs)
      {
        return it==rhs.it;
      }
      T& operator*()
      {
        return *it;
      }
    };
    
    int main () {
      vector<int> foo,bar;
      vector<int>::iterator it;
    
      // set some values:
      for (int i=1; i<7; i++) foo.push_back (i*10); //  foo: 10 20 30 40 50 60
    
      bar.resize(foo.size());     // allocate space
      transform (double_iterator<int>(foo.begin()),double_iterator<int>(foo.end()), double_iterator<int>(bar.begin()), op_increase);
                                                      // bar: 11 0 31 0 51 0
    
      cout << "bar contains:";
      for (it=bar.begin(); it!=bar.end(); ++it)
        cout << " " << *it;
    
      cout << endl;
      return 0;
    }
    


  • Eleganter vielleicht, schneller nicht wirklich. Aber im Endeffekt furzegal. Die STL-Algorithmen zu verwenden, nur damit man sie verwendet hat, ist nicht besonders sinnvoll.



  • Seikilos schrieb:

    Ich dachte nur, es geht auch eleganter mit stl.

    Stichwort elegant 🕶 - na ja es gibt Möglichkeiten:

    #include <boost/iterator/iterator_adaptor.hpp>
    
    #include <algorithm> // transform
    #include <iostream>
    #include <vector>
    #include <iterator> // ostream_iterator
    #include <functional> // plus
    
    struct CycleIter : public boost::iterator_adaptor< CycleIter, std::vector< double >::const_iterator
        , boost::use_default, boost::forward_traversal_tag >
    {
        CycleIter( std::vector< double >::const_iterator first, std::vector< double >::const_iterator last )
            : iterator_adaptor_( first )
            , m_begin( first )
            , m_end( last )
        {}
        void increment()
        {
            if( ++base_reference() == m_end )
                base_reference() = m_begin;
        }
    private:
        std::vector< double >::const_iterator m_begin;
        std::vector< double >::const_iterator m_end;
    };
    
    int main()
    {
        using namespace std;
    
        vector< double > vertices( 8, 10.0 );
        vector< double > offset(2);
        offset[0] = 1.0; // offset.x()
        offset[1] = 2.0; // offset.y()
        transform( vertices.begin(), vertices.end(), CycleIter( offset.begin(), offset.end() ), 
            ostream_iterator< double >( cout << "Ergebnis: ", " "), plus< double >() );
        // Bem.: statt ostream_iterator< double >(..) ginge natürlich auch vertices.begin()
        cout << endl;
        return 0;
    }
    

    Die Ausgabe ist:

    Ergebnis: 11 12 11 12 11 12 11 12
    

    .. ob's brauchbar ist?


Anmelden zum Antworten