\documentclass[uebung]{../../../lecture} \title{IPI: Übungsblatt 11} \author{Samuel Weidemaier, Christian Merten} \begin{document} \begin{tabular}{|c|m{1cm}|m{1cm}|m{1cm}|m{1cm}|@{}m{0cm}@{}} \hline Aufgabe & \centering A1 & \centering A2 & \centering A3 & \centering $\sum$ & \\[5mm] \hline Punkte & & & & & \\[5mm] \hline \end{tabular} \begin{aufgabe} Polynomschablone. Die vorgegebene Implementation der \lstinline{SimpleArray} Klasse ist in \textit{simplearray.cc} zu finden. In \textit{polynomial.cc} befindet sich die Implementation der verallgemeinerten \lstinline{Polynomial} Klasse. \begin{lstlisting}[language=C++, captionpos=b, title=polynomial.cc] #include #include #include "simplearray.cc" template class Polynomial: public SimpleArray { public: // konstruiere Polynom vom Grad n Polynomial (int n); // Default-Destruktor ist ok // Default-Copy-Konstruktor ist ok // Default-Zuweisung ist ok // Grad des Polynoms int degree(); // Auswertung T eval (T x); // Addition von Polynomen Polynomial operator+ (Polynomial q); // Multiplikation von Polynomen Polynomial operator* (Polynomial q); // Gleichheit bool operator== (Polynomial q); // drucke Polynom void print (); }; // Constructor template Polynomial::Polynomial(int n) : SimpleArray::SimpleArray(n+1,0.0) {} // Grad auswerten template int Polynomial::degree () { return SimpleArray::maxIndex(); } // Addition von Polynomen template Polynomial Polynomial::operator+ (Polynomial q) { int nr=degree(); // mein grad if (q.degree()>nr) nr=q.degree(); Polynomial r(nr); // Ergebnispolynom for (int i=0; i<=nr; i=i+1) { if (i<=degree()) r[i] = r[i]+(*this)[i]; // add me to r if (i<=q.degree()) r[i] = r[i]+q[i]; // add q to r } return r; } // Multiplikation von Polynomen template Polynomial Polynomial::operator* (Polynomial q) { Polynomial r(degree()+q.degree()); // Ergebnispolynom for (int i=0; i<=degree(); i=i+1) for (int j=0; j<=q.degree(); j=j+1) r[i+j] = r[i+j] + (*this)[i]*q[j]; return r; } // Drucken template void Polynomial::print () { if (degree()<0) std::cout << 0; else std::cout << (*this)[0]; for (int i=1; i<=SimpleArray::maxIndex(); i=i+1) std::cout << " + " << (*this)[i] << "*x^" << i; std::cout << std::endl; } int main() { Polynomial p(2); p[0] = 1.0; p[1] = 1.0; p.print(); p = p*p; p.print(); Polynomial q(3); q[0] = 2.0; q[1] = -1.0; q[3] = 4.0; q.print(); }\end{lstlisting} \end{aufgabe} \begin{aufgabe} Vektorimplementation \begin{lstlisting}[language=C++, captionpos=b, title=vectors.cc] #include #include #include #include // a class implementing a mathematical vector template class Vector { public: // constructor, initializes vector with zeroes of length N Vector() : _vector(N, 0) {} // initialize by passing vector vec to initialize the vector object with Vector(std::vector vec) { _vector = vec; } // copy constructor, assignment, destructor not needed // all handled by std::vector // reference [] operator, used for assignments K& operator[](int i) { return _vector[i]; } // const ref [] operator K operator[](int i) const { return _vector[i]; } // return an iterator pointing to the first element of the underlying std::vector typename std::vector::const_iterator begin() const { return _vector.begin(); } // return an iterator pointing to the last element of the underlying std::vector typename std::vector::const_iterator end() const { return _vector.end(); } // dot product of two vectors of the same length template K operator*(const Vector& y) { std::vector result(N); // new empty std::vector of correct size // now multiply one element of the first with one of the second vector std::transform(_vector.begin(), _vector.end(), y.begin(), result.begin(), std::multiplies()); // and sum up the products return std::accumulate(result.begin(), result.end(), 0); } // vector addition template Vector operator+(const Vector& y) { std::vector result(N); // new empty std::vector of correct size // add elements by adding one element of the first to one of the second vector std::transform(_vector.begin(), _vector.end(), y.begin(), result.begin(), std::plus()); return Vector(result); } // return the maximum value in the vector K max() { return *std::max_element(_vector.begin(), _vector.end()); } // return the minimum value in the vector K min() { return *std::min_element(_vector.begin(), _vector.end()); } // return the average value of the vector K average() { // sum up all elements K sum = std::accumulate(_vector.begin(), _vector.end(), 0); return sum / N; // and divide by length } // run the specified function on each vector element template Vector map(F op) const { std::vector result(N); // initialize new std::vector of correct size // run op on each element std::transform(_vector.begin(), _vector.end(), result.begin(), op); return Vector(result); } private: std::vector _vector; // the private std::vector container }; // print vector template std::ostream& operator<<(std::ostream& os, const Vector& v) { os << "["; // opening brackets for (size_t i = 0; i < N; i++) { os << v[i]; // print element if(i < N-1) { os << ", "; // if not the last one, add comma } } os << "]"; // closing brackets return os; } // scalar multiplication for vector * scalar template Vector operator*(const Vector& v, const K2 k) { // multiply a scalar value using the generic map function return v.map(std::bind1st(std::multiplies(), k)); } // scalar multiplication for scalar * vector template Vector operator*(const K2 k, const Vector& v) { return v * k; } // example function double foo(double val) { return val*2+3; } // testing out functionalities int main() { Vector a; Vector b; a[0] = 0; a[1] = 0; a[2] = 4; b[0] = 42; b[1] = -20; b[2] = 3; std::cout << "Vektor a: " << a << ", Vektor b: " << b << std::endl; std::cout << "a+b: " << a+b << std::endl; std::cout << "a*b: " << a*b << std::endl; std::cout << "42*b: " << 42*b << std::endl; std::cout << "b*42: " << b*42 << std::endl; std::cout << "max(a): " << a.max() << std::endl; std::cout << "min(a): " << a.min() << std::endl; std::cout << "mean(a): " << a.average() << std::endl; std::cout << "foo angewendet auf b: " << b.map(foo) << std::endl; }\end{lstlisting} \end{aufgabe} \begin{aufgabe}[Funktoren und statischer Polymorphismus] \begin{enumerate}[(a)] \item Integration mittels Trapezregel für $n = 10$, $n = 100$ und $n = 1000$ beispielsweise ausgerechnet. \begin{lstlisting}[language=C++, captionpos=b, title=integration.cc] #include #include // Implementiert die Trapezregel mit der Intervallanzahl n // der unteren Integrationsgrenze a, der oberen Integrationsgrenze b // und ueber die Funktion f template double trapezregel(int n, double a, double b, Function f) { double h = (b-a)/n; // berechne intervalllaenge double accum = 0; for (int i = 1; i < n; i++) { accum += f(a + i*h); // fuehre die summe aus } return h/2 * (f(a) + 2*accum + f(b)); // wende formel an } // beispiel fuer funktor class Wurzel { public: double operator()(double x) { return std::sqrt(x); } }; int main() { Wurzel f; // fuehre integration fuer verschiedene Intervallanzahlen n aus double integ10 = trapezregel(10, 0, 1.5, f); double integ100 = trapezregel(100, 0, 1.5, f); double integ1000 = trapezregel(1000, 0, 1.5, f); std::cout << "Integral ueber sqrt(x) von 0 bis 1.5" << std::endl; std::cout << "n = 10: " << integ10 << std::endl; std::cout << "n = 100: " << integ100 << std::endl; std::cout << "n = 1000: " << integ1000 << std::endl; }\end{lstlisting} \item Vorteile der Verwendung von statischem Polymorphismus gegenüber dynamischem Polymorphismus: \begin{itemize} \item Auswertung zum Zeitpunkt des Kompilierens führt zu besserer Optimierung und Fehlererkennung durch den Compiler. \item Weniger Overhead durch VTABLE's, etc. \item Deshalb effizienter und schneller \end{itemize} Nachteile: \begin{itemize} \item Potentiell unübersichtlicher \item Größe der Programme als kompilierte Binärdatei ist größer \end{itemize} \end{enumerate} \end{aufgabe} \end{document}