0x07 Temat założony przez niniejszego użytkownika |
Obliczenia z użyciem klasy vector oraz funkcji std::transform. » 2017-09-13 12:39:13 Cześć Wam drodzy użytkownicy. Przy okazji lektury dotyczącej porównania klas vector oraz valarray przy przeprowadzaniu obliczeń, natknąłem się na przykład: (obiekty varr są typu valarray<double>): varr3 = 10.0 * ((varr1 + varr2) / 2.0 + varr1 * cos(varr2)); Oczywiście interfejs klasy valarray zezwala na wykonywanie tego typu obliczeń z użyciem operatorów arytmetycznych. Autor lektury zachęcał jednak do wykonania tych samych obliczeń przy użyciu kontenera std::vector. Co prawda uporałem się tym be większych problemów (co mnie szczególnie niepokoi i skłania do refleksji), jednakże chciałbym zapytać kogoś bardziej doświadczonego o poprawność mojego rozumowania. Interesuje mnie także, czy zamieszczony poniżej kod można w jakiś sposób uprościć (poza osobną inicjalizacją insert_iterator), np. sprowadzając do trzech lub mniej instrukcji itd. template < typename T > T kos( T const & val ) { return static_cast < double >( cos( val ) ); }
int const max = 5; double arry[ max ] = { 1, 2, 3, 4, 5 }; double arry2[ max ] = { 6, 7, 8, 9, 10 }; vector < double > vec1( arry, arry + max ), vec2( arry2, arry2 + max ), vec3; vector < double > temp; transform( vec1.begin(), vec1.end(), vec2.begin(), insert_iterator < vector < double >>( vec3, vec3.begin() ), plus < double >() ); transform( vec3.begin(), vec3.end(), vec3.begin(), bind1st( divides < double >(), 2.0 ) ); transform( vec2.begin(), vec2.end(), insert_iterator < vector < double >>( temp, temp.begin() ), kos < double > ); transform( temp.begin(), temp.end(), vec1.begin(), temp.begin(), multiplies < double >() ); transform( vec3.begin(), vec3.end(), temp.begin(), vec3.begin(), plus < double >() ); transform( vec3.begin(), vec3.end(), vec3.begin(), bind1st( plus < double >(), 10 ) ); copy( vec3.begin(), vec3.end(), ostream_iterator < double, char >( cout, " " ) );
Wynik wyj ś ciowy: 11.2459 11.73 9.74532 6.50933 5.93798
Musiałem przekształcić funkcję cos na kos, ponieważ użycie "surowej" wersji cos<double> powodowało błąd kompilacji (w sumie nawet nie wiem dlaczego tak się dzieje, lecz podobnie jest w przypadku innych funkcji wykonujących operacje matematyczne na liczbach, np. log<>). Będę bardzo wdzięczny za jakiekolwiek zaangażowanie w ten temat. Kontakt z innymi pasjonatami C++ z pewnością pomoże mi nabyć kolejne doświadczenie w procesie nauki tego języka. Serdecznie pozdrawiam, 0x07 |
|
Monika90 |
» 2017-09-13 13:03:12 Wektor można zainicjalizować w prostszy sposób vector < double > vec1 = { 1, 2, 3, 4, 5 };
bind1st zostało usunięte z C++, od dawna dostępne są lepsze zamienniki. Ja bym zrobiła tak vector < double > vec1 = { 1, 2, 3, 4, 5 }; vector < double > vec2 = { 6, 7, 8, 9, 10 }; vector < double > vec3;
transform( vec1.begin(), vec1.end(), vec2.begin(), back_inserter( vec3 ),[]( double x, double y ) { return 10.0 *(( x + y ) / 2.0 + x * cos( y ) ); } );
for( const auto & x: vec3 ) cout << x << ' ';
Wynik jest zupełnie inny niż twój 44.6017 60.078 50.635 28.5548 33.0464 |
|
0x07 Temat założony przez niniejszego użytkownika |
» 2017-09-13 14:28:58 @Monika90 Bardzo dziękuję za zaangażowanie oraz profesjonalną poradę. Niestety napisana przez Ciebie konstrukcja, na chwilę obecną, wykracza poza granice mojego zrozumienia języka C++. Nie spotkałem się jeszcze z wykorzystaniem funkcji transform (ani żadnej innej funkcji STL) wraz z blokiem kodu umieszczonym pomiędzy nawiasami klamrowymi. Obce jest mi także wyrażenie stanowiące ostatni argument transform ([](double, double). Tym niemniej pokazałaś mi, jak bardzo można uprościć mój kod, za co jestem ogromnie wdzięczny. Jeszcze dzisiaj przestudiuję napisaną przez Ciebie konstrukcję. Jestem zaskoczony, że bind1st (oraz, jak zakładam, klasy binder1st i binder2nd) wyszło z użycia. Czy mogłabyś, proszę, wskazać na jeden z zamienników, o których wspomniałaś? Byłby to dla mnie doskonały punkt zaczepiania w toku dalszej nauki. Oczywiście sam także poszukam informacji na ten temat w Internecie. Wynik otrzymany przez Ciebie rzeczywiście jest prawidłowy, co potwierdzają operacje arytmetyczne przeprowadzone z wykorzystaniem klasy valarray. Postanowiłem odszukać błędy we własnym kodzie. Oto one:
transform( vec3.begin(), vec3.end(), vec3.begin(), bind1st( divides < double >(), 2.0 ) );
transform( vec3.begin(), vec3.end(), vec3.begin(), bind1st( multiplies < double >(), 10 ) );
Po wprowadzonych zmianach wynik mój wynik jest dokładnie taki sam jak Twój. Twoja odpowiedź bardzo mi pomogła, za co jeszcze raz serdecznie dziękuję. PS Wiem o inicjalizacji klamrowej. Stosuję iteratory(w przypadku tablic wbudowanych to raczej wskaźniki, tak?) przy inicjalizacji, aby oswoić się z ich używaniem; są to dla mnie elementy języka wciąż nowe i nie do końca poznane. |
|
Luq |
» 2017-09-13 14:54:59 Obce jest mi także wyrażenie stanowiące ostatni argument transform ([](double, double). | http://en.cppreference.com/w/cpp/language/lambdaJestem zaskoczony, że bind1st (oraz, jak zakładam, klasy binder1st i binder2nd) wyszło z użycia. Czy mogłabyś, proszę, wskazać na jeden z zamienników, o których wspomniałaś? | std::bind |
|
mokrowski |
» 2017-09-13 16:53:23 Jeszcze można zauważyć że kontener wynikowy jest tego samego rozmiaru co kontenery danych. Tworzenie go z podaną wielkością spowoduje że niepotrzebny będzie back_inserter a wystarczy begin(). To i tak przy tej ilości danych nie będzie miało dużego znaczenia ale warto takie (tanie) optymalizacje wykonać :-) Jak zawsze. Takie pomysły warto sprawdzić co do czasu wykonania w danym środowisku.
@0x07 dla tablic od C++11 masz jeszcze opakowanie iteratora początkowego i końcowego przez std::begin(tablica) i std::end(tablica). |
|
0x07 Temat założony przez niniejszego użytkownika |
» 2017-09-14 14:28:32 Dziękuję wszystkim za zaangażowanie i udzielone wskazówki. Wasze doświadczenie bardzo pomoże mi w dalszej nauce C++. Uważam temat za wyczerpany, dlatego też go zamykam.
Serdecznie pozdrawiam 0x07 |
|
« 1 » |