Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Obliczenia z użyciem klasy vector oraz funkcji std::transform.

Ostatnio zmodyfikowano 2017-09-14 14:28
Autor Wiadomość
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.

C/C++
template < typename T >
T kos( T const & val ) { return static_cast < double >( cos( val ) ); }
C/C++
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, " " ) );
C/C++
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
P-164841
Monika90
» 2017-09-13 13:03:12
Wektor można zainicjalizować w prostszy sposób
C/C++
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
C/C++
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
P-164842
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:
C/C++
// w ostatnim argumencie zamiast bind1st powinna znaleźć się funkcja bind2nd
// w poprzedniej wersji program dzielił kolejno wartość 2.0 przez wszystkie elementy wektora
transform( vec3.begin(), vec3.end(), vec3.begin(), bind1st( divides < double >(), 2.0 ) );
C/C++
// w ostatnim argumencie zamiast funkcji plus<double> powinna znaleźć się funkcja multiplies<double>
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.
P-164843
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/lambda

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ś?
std::bind
P-164844
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).
P-164845
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
P-164873
« 1 »
  Strona 1 z 1