Entwicklung einer einfachen Matrizenklasse



  • std::max() liefert das größte Element aus init und das Lambda ist der Komperator der die Elemente vergleicht. Verwendet wird das Dingsti aus init das die größte .length() hat.



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Was genau macht eigentlich die dortige Lambda-Funktion(?)?

    Ich glaube der Code stammt von mir, daher bin ich mal so frei:

    template <typename T>
    Matrix<T>::Matrix(std::initializer_list<std::initializer_list<T>> init)
    : Matrix{ 
        init.size(),
        std::max(
            init,
            [](const auto& a, const auto& b) { //Lambda-Funktion?
                return a.size() < b.size();
            }
        ).size()
      }
    {
    ...
    

    Die Lambda-Funktion vergleicht zwei std::initializer_list<T> nach Länge (Anzahl der Elemente). Siehe Doku zu std::max (Variante 4 wird hier verwendet).

    Der std::max(...)-Ausdruck liefert diejenige, innere std::initializer_list<T> zurück, welche die meisten Elemente hat (die Listen können unterschiedlich lang sein). Mit deren Größe wird die Anzahl der Spalten insitialisiert (Delegating Constructor-Aufruf Matrix{ rows, columns }). Die Matrix soll schliesslich immer rechteckig sein.

    Bei Initialisierung mit geschachtelten Initializer Lists wie z.B.

    Matrix m{
        { 1, 2 },
        { 3, 4, 5 },
        { 6 }
    };
    

    wird hier zunächst eine wegen des Matrix(rows, columns)-Konstruktors Value-Initialisierte (effektiv genullte, mithilfe des Konstruktors von std::vector<T>) 3x3-Matrix erzeugt, und die angegebenen Elemente dann mit dem weiteren Code des Konstruktors in die Matrix geschrieben:

    {
        std::size_t i = 0;
        for (const auto& row : init)
        {
            if (i >= rows())
                break;
            std::size_t j = 0;
            for (const auto& value : row)
            {
                if (j >= columns())
                    break;
                (*this)(i, j) = value;
                ++j;
            }
            ++i;
        } 
    }
    

    Die resultierende Matrix bei meiner Beispiel-Initialisierung oben wäre also:

    1 2 0
    3 4 5
    6 0 0
    

    Dieses Verhalten ist analog zur aggregate initialization, wo es auch so ist, dass wenn zu wenige Elemente in der Liste angegeben werden, die restlichen Elemente ebenfalls Value-Initialisiert werden (principle of least surprise).


  • Mod

    @Caligulaminus sagte in Entwicklung einer einfachen Matrizenklasse:

    @SeppJ, @wob Könntet Ihr bitte kurz anreißen wie Ihr das macht?

    @SeppJ sagte in [Entwicklung einer einfachen Matrizenklasse]

    Umlaute auf rechtes Alt + Vokal legen.

    @wob sagte in [Entwicklung einer einfachen Matrizenklasse]

    Tipp: die ansonsten nutzlose Feststelltaste (caps lock) kann man wunderbar zur Tastaturlayoutumstellung verwenden.

    Wie das in Linux geht, hat wob schon erklärt. Allgemein gibt es dort sehr gute und einfache Möglichkeiten. Unter Windows benutze ich auf meinem persönlichen Rechner ein custom Tastaturlayout. Da gibt's einen Editor von Microsoft für, das man dann als ganz normales Tastenlayout installieren kann. Wenn ich auf Rechnern unterwegs bin, wo ich kein Admin bin, dann nutze ich Autohotkey, weil das auch auf Userebene funktioniert. Das funktioniert zu 99% so gut wie ein richtiges Tastaturlayout, aber im Schnitt einmal im Monat verschluckt es sich mal kurz.



  • @Swordfish
    @Finnegan

    Toll. Danke, die Funktion verstehe ich jetzt, hätte dafür wieder ellenlangen Code geschrieben. Nur an die Syntax muss ich mich noch gewöhnen.



  • @SeppJ
    Danke fürs Feedback. Dann werde ich mich mal nach diesem Editor umschauen...



  • Damals mit Windows habe ich https://www.microsoft.com/en-us/download/details.aspx?id=22339 verwendet. Entweder meint @SeppJ den - oder es gibt inzwischen irgendwas Neueres.


  • Mod

    @wob sagte in Entwicklung einer einfachen Matrizenklasse:

    Damals mit Windows habe ich https://www.microsoft.com/en-us/download/details.aspx?id=22339 verwendet. Entweder meint @SeppJ den - oder es gibt inzwischen irgendwas Neueres.

    Ja, klingt richtig. ist lange her. Das hab' ich noch unter Windows 7 gemacht und seither bloß das neue Layout auf neue Rechner migriert.



  • Hi, ich möchte gerne hier irgendwie weitermachen. Was mir noch fehlt, ist ein Konstruktor, der einen std::vectorannimmt. Hatte gehofft, die ìnitializer_list nimmt einen an, aber das ist wohl nicht so einfach.

    Deshalb habe ich noch folgenden:

    Mat2D_T(const std::vector<std::vector<valType>>& elements)
    		: Mat2D_T{ elements.size(), elements.front().size() }
    	{
    		for (size_t r = 0; r < rows(); ++r)
    		{
    			for (size_t c = 0; c < columns(); ++c)
    			{
    				if (c >= elements[r].size()) break;
    				const auto v = elements[r][c];
    				setElement(r, c, v); //EDIT: geht natürlich auch (*this)(r, c) = v; ;-)
    			}
    		}
    	}
    

    Ich nehme den ersten Vector als Spaltenanzahl. Um unsinnige Eingaben abzufangen, müsste das doch genügen?



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Um unsinnige Eingaben abzufangen, müsste das doch genügen?

    Eine Matrix als vector von vectoren ist an sich schon eine unsinnige Eingabe. ^^



  • Schon deshalb blöd, weil ich eigentlich zB dahin will:

    	std::vector<std::vector<float>> pArr = { {0., 0. }, {15., 0.}, {15., 9. }, {7.5, 13.}, {0., 9. } };
    		std::vector<Mat2D_T<float>> mVec;
    
    
    		for (const auto& pVec : pArr)
    		{
    			Mat2D_T<float> mt{ pVec };
    			mVec.push_back(mt);
    		}
    


  • @zeropage Ich kapiers nicht. Du willst Zeilenvektorenmatrizen aus std::vector<sonstwas> basteln und dann eine Matrix daraus?



  • Vergiss es. Bin betrunken. Punkte sollen eine Matrix sein. Und dann gibt es Matrizen, die ich mit Punkten multiplizieren kann. Habe vorgegriffen. Natürlich erst Matrizen erstellen, dann Punkte, und dann nochmal multiplizieren.

    Also vergiss alles. Erst Matrizen, dann Punkte.





  • Wieder nüchtern.
    std::vectorhat sich erledigt, die initializer_listreicht. Aber noch etwas zu print-Methoden. Ich habe zwar eine Ausgabe,

    template <typename T>
    std::ostream& operator<<(std::ostream& stream, const Mat2D_T<T>& mT)
    {
    	stream << typeid(T).name() << " " << mT.rows() << "x" << mT.columns();
    	stream << " { ";
    	for (std::size_t r = 0; r < mT.rows(); ++r)
    	{
    		if (r > 0) stream << ", ";
    		stream << "{ ";
    		for (std::size_t c = 0; c < mT.columns(); ++c)
    		{
    			if (c > 0) stream << ", ";
    			stream << mT(r, c);
    		}
    		stream << " }";
    	}
    	stream << " }\n";
    	return stream;
    }
    

    möchte aber gerne mal eine formatierte. Da ich mir vorstellen kann, das die nicht in eine Template-Klasse gehören, habe ich sie in ein namespacegepackt.
    Ich zeig das lieber jetzt, bevor es irgendwann auftaucht und dann extra korrigiert werden muss.

    namespace Mat2D
    {
    	void print(const Mat2D_T<int>& mT, const int cwidth = 4, std::ostream& stream = std::cout);
    	void printElement(const Mat2D_T<int>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream = std::cout);
    	void print(const Mat2D_T<float>& mT, const int cwidth = 4, std::ostream& stream = std::cout);
    	void printElement(const Mat2D_T<float>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream = std::cout);
    	void print(const Mat2D_T<double>& mT, const int cwidth = 4, std::ostream& stream = std::cout);
    	void printElement(const Mat2D_T<double>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream = std::cout);
    }
    

    Die verschiedene Ausgabe von int und fließkomma ist nur wegen setprecision().

    void Mat2D::printElement(const Mat2D_T<int>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream)
    {
    	stream << "(" << row << "," << column << "):";
    	stream << std::setw(cwidth) << mT.element(row, column);
    }
    
    void Mat2D::printElement(const Mat2D_T<float>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream)
    {
    	const int cp = 4;
    	const int cpwidth = cwidth + cp;
    	stream << "(" << row << "," << column << "): ";
    	stream << std::setw(cpwidth) << std::fixed << std::setprecision(cp) << mT.element(row, column);
    }
    


  • Dieser Beitrag wurde gelöscht!


  • So, jetzt nochmal.
    Gut, dann jetzt, wie man zB eine Einheitsmatrix erstellt. Da hätte ich zwei Vorschläge, der erste erscheint mir besser. Kann aber sein, das es besseres gibt, was mir nur nicht einfällt.
    Ich lass das jetzt auch stehen, auch wenn mir in der Zwischenzeit was anderes einfällt

    template<typename T>
    class UnitMat_T : public Mat2D_T<T>
    {
    public:
    	using size_t = std::size_t;
    
    	UnitMat_T(const size_t size)
    		: Mat2D_T<T>(size, size)
    	{
    		for (size_t n = 0; n < size; ++n)
    			(*this)(n, n) = static_cast<T>(1);
    	}
    };
    
    
    template<typename T>
    Mat2D_T<T>getUnitMat(const std::size_t size)
    {
    	Mat2D_T<T> ut(size, size);
    
    	for (std::size_t n = 0; n < size; ++n)
    		ut(n, n) = static_cast<T>(1);
    
    	return ut;
    }
    


  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    So, jetzt nochmal.
    Gut, dann jetzt, wie man zB eine Einheitsmatrix erstellt. Da hätte ich zwei Vorschläge, der erste erscheint mir besser. [...]

    template<typename T>
    class UnitMat_T : public Mat2D_T<T>
    

    Also ich finde den zweiten besser. UnitMat_T ist wie ein float_value_1_t. Wenn du keinen extrem guten Grund hast, für einen speziellen Wert einen eigenen Typen zu haben, dann lass es. Es wird noch genug andere Gelegenheiten geben, sich das Leben selbst schwer zu machen 😉

    Eigener Typ für quadratische Matrizen könnte ich noch akzeptieren. Die haben ja durchaus etwas andere algebraische Eigenschaften, die einen zusätzlichen Typen rechtfertigen können.



  • Ok. Und die Funktion (dann auch ohne Template?) getUnitMat()oder gleichUnitMat()kann ich dann in den Mat2Dnamespace schieben?



  • Dieser Beitrag wurde gelöscht!


  • Also so? (mit meinem haben wollen Point). Obwohl eigentlich Blödsinn, ich möchte ja einen Typ Point. Aber ich lösch das nicht schon wieder.

    namespace Mat2D
    {
    	template<typename val_t>
    	Mat2D_T<val_t> UnitMat(const std::size_t size)
    	{
    		Mat2D_T<val_t> ut(size, size);
    
    		for (std::size_t n = 0; n < size; ++n)
    			ut(n, n) = static_cast<val_t>(1);
    
    		return ut;
    	}
    	template<typename val_t>
    	Mat2D_T<val_t> Point2D(const val_t x, const val_t y)
    	{
    		Mat2D_T<val_t> p{ { x, y, static_cast<val_t>(1) } };
    		return p;
    	}
    	template<typename val_t>
    	Mat2D_T<val_t> Point3D(const val_t x, const val_t y, const val_t z)
    	{
    		Mat2D_T<val_t> p{ { x, y, z } };
    		return p;
    	}
    
    	void print(const Mat2D_T<int>& mT, const int cwidth = 4, std::ostream& stream = std::cout);
    	void printElement(const Mat2D_T<int>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream = std::cout);
    	void print(const Mat2D_T<float>& mT, const int cwidth = 4, std::ostream& stream = std::cout);
    	void printElement(const Mat2D_T<float>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream = std::cout);
    	void print(const Mat2D_T<double>& mT, const int cwidth = 4, std::ostream& stream = std::cout);
    	void printElement(const Mat2D_T<double>& mT, const size_t row, const size_t column, const int cwidth, std::ostream& stream = std::cout);
    }
    
    
    

Anmelden zum Antworten