Visualizzazione dei risultati da 1 a 8 su 8
  1. #1
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    41

    [C++] Stroustrup 10.6 distribution classes

    Salve, mi sto esercitando sull'utilizzo delle classi risolvendo alcuni esercizi di Stroustrup. Purtroppo non ho le soluzioni (non so neanche se esistono) e facendo da autodidatta non riesco a capire se sto facendo bene o meno, quindi chi avrà la pazienza di leggere e darmi un riscontro avrà la mia gratitudine (che fortuna eh...).
    Il programma sembra funzionare correttamente, ma ovviamente non so autovalutare il mio lavoro d'implementazione. L'esercizio richiedeva la scrittura di una classe per la generazione di random variable estratte dalla distribuzione uniforme e esponenziale.
    Qui riporto l'header e il source, grazie a chi vorrà commentare.
    In particolare ho dubbi sulla ripetizione degli header nei due file (forse il cpp può evitare di riscrivere gli header già inclusi nel .header annesso?, in questo caso solo <vector>, ma la domanda è generale, cioè c'è una prassi?). Inoltre non so quale implementazione della funzione che genera un vettore di r.v. è il migliore. Grazie

    Header

    codice:
    #ifndef UNIFORM_DISTRIBUTION_H
    #define UNIFORM_DISTRIBUTION_H
    
    
    #include <vector>
    
    
    class UniformDist {
        private:
                double _a, _b; //distributions' interval
        public: 
                UniformDist(); //default constructor (default interval [0,1])
                UniformDist(const double& a_, const double& b_); //parameter constructor
                UniformDist(const UniformDist& rhs); //copy constructor
                virtual ~UniformDist(); //virtual destructor
                double get_a(); //get _a
                double get_b(); //get _b
                // extract a realization of a r.v. having a uniform distribution on the [a,b] interval
                void gen_samples(std::vector<double>& Vsamples);
                void gen_samples(const int& Nsamples, std::vector<double>& Vsamples); 
    };
    
    
    #endif
    Source

    codice:
    #ifndef UNIFORM_DISTRIBUTION_CPP
    #define UNIFORM_DISTRIBUTION_CPP
    
    #include <cstdlib>
    #include <vector>
    #include <ctime>
    #include "UniformDist.h"
    
    
    //default constructor
    UniformDist::UniformDist(){
        srand(time(0));
        _a = 0;
        _b = 1;
    }
    
    
    //parameter constructor
    UniformDist::UniformDist(const double& a_, const double& b_){
        srand(time(0));
        _a = a_;
        _b = b_;
    }
    
    
    //copy constructor
    UniformDist::UniformDist(const UniformDist& rhs){
         _a = rhs.get_a();
         _b = rhs.get_b();
    }
    
    
    //virtual destructor
    UniformDist::~UniformDist() {};
    
    
    //getters
    double UniformDist::get_a(){
        return _a;
    }
    
    
    double UniformDist::get_b(){
        return _b;
    }
    
    
    //return a vector of Nsample samples from the uniform distribution on the [_a,_b] interval
    void UniformDist::gen_samples(const int& Nsamples, std::vector<double>& Vsamples){
        srand(time(0));
        Vsamples.assign(Nsamples, 0.0);
        for(std::vector<double>::iterator iter = Vsamples.begin(); iter != Vsamples.end(); iter++){
            *iter = (_b -_a)*(rand() / (double)RAND_MAX) +_a;
        }
    }
    
    
    /*//return a vector of Nsample samples from the uniform distribution on the [_a,_b] interval
    void UniformDist::gen_samples(const int& Nsamples, std::vector<double>& Vsamples){
        srand(time(0));
        for(int i=0;i<Nsamples;i++){
            Vsamples.push_back((_b-_a)*(rand()/(double)RAND_MAX) + _a);
        }
    }*/
    #endif
    Ultima modifica di Toxotes; 29-01-2014 a 13:42

  2. #2
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    41
    C'è un errore nel copy constructor corretto:

    codice:
    UniformDist::UniformDist(const UniformDist& rhs){
        _a = rhs.get_a();
        _b = rhs.get_b();
    }
    Inoltre la prima gen_samples è stata cancellata.
    Ultima modifica di Toxotes; 29-01-2014 a 14:57

  3. #3
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Quote Originariamente inviata da Toxotes Visualizza il messaggio
    ma ovviamente non so autovalutare il mio lavoro d'implementazione.
    Il fatto è che non esiste un modo di procedere unico, anche se è possibile riconoscere vari "stili" di programmazione. Per cui il mio giudizio vale quanto il tuo se il codice funziona.
    Un valido aiuto sullo "stile" lo trovi qui:
    http://www.eptacom.net/pubblicazioni/cpp_stile/all.html
    che se anche un po' datato è comunque un buon inizio.

    In particolare ho dubbi sulla ripetizione degli header nei due file (forse il cpp può evitare di riscrivere gli header già inclusi nel .header annesso?, in questo caso solo <vector>, ma la domanda è generale, cioè c'è una prassi?).
    No, nessuna prassi. Del resto se un header file è rindondante ci pensa il precompilatore a toglierlo. Piuttosto le righe:
    codice:
    #ifndef UNIFORM_DISTRIBUTION_CPP
    #define UNIFORM_DISTRIBUTION_CPP
    andrebbero tolte. Un file .cpp viene allegato in progetto una volta sola, non più di una come può avvenire nel corrispettivo .h per cui non ha senso porre una include guard in un file .cpp

    Inoltre non so quale implementazione della funzione che genera un vettore di r.v. è il migliore. Grazie
    Da qui inizia la mia personale visione della cosa: nessuna delle due mi piace.
    Non sono generiche. Prendono un parametro fisso (il vector) che è un pò arbitrario, inoltre non consentono l'append di valori. Se un domani tu avessi bisogno di riempire una list, un set o un semplice array dovresti fornire degli overload per ognuno di questi tipi. Il mio consiglio è di lavorare sulla genericità prendendo spunto dalle interfacce degli algoritmi standard (utilizzano tutti solo iteratori e function objects).

    Dovessi implementare io una classe simile scriverei:
    codice:
    class UniformDist {
        private:
                double _a, _b; //distributions' interval
        public: 
                UniformDist(); //default constructor (default interval [0,1])
                UniformDist(const double& a_, const double& b_); //parameter constructor
                double get_a() { return _a; } //get _a
                double get_b() { return _b;}  //get _b
                double operator()();            
    };
    
    //default constructor
    inline UniformDist::UniformDist(){
        srand(time(0));
        _a = 0;
        _b = 1;
    }
    
    //parameter constructor
    inline UniformDist::UniformDist(const double& a_, const double& b_) {
        srand(time(0));
        _a = a_;
        _b = b_;
    }
    
    inline UniformDist::operator()() {
        return (_b -_a)*(rand() / (double)RAND_MAX) +_a;
    }
    
    ///////////////////////////
    
    int main (etc) {
    
        vector <double> v1(10);
        std::generate(v1.begin(),v1.end(), UniformDist(1,3));
    
        vector <double> v2;
        std::generate_n(std::back_inserter(v1),20, UniformDist(1,3));
    
        list<double> ls;
        std::generate_n(std::back_inserter(ls),20, UniformDist(1,3));
    
        set<double> myset(12);
        std::generate(myset.begin(),myset.end(), UniformDist(1,3));
    }
    Ma come ho detto è solo una delle possibili implementazioni (e un personale punto di vista).
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  4. #4
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    41
    First of all, grazie mille per l'attenzione shodan.

    Allora effettivamente il tuo punto di vista è ragionevolissimo (in effetti il vector è restrittivo e alla lista si potrebbero aggiungere mappe e chissà cos'altro).

    Prendo spunto per ripassarmi il concetto delle funzioni inline.

    I functors in effetti sono entità che dovrei imparare a maneggiare con più disinvoltura.

    Da quel che vedo quindi l'approccio (che mi piace molto) è del tipo: fai quello che devi aggiungendo il meno possibile per lavorare su oggetti standard che hanno già metodi ottimi e pensanti per lavorare efficacemente.
    Cmq quest'indizio mi sembra meno soggettivo: mi confermi che ti sembra buona norma (potremmo prenderla come regola generale per l'apprendista programmatore) utilizzare solo gli iteratori, functors o metodi di una determinata interfaccia standard per "smanettarci su" senza inventarsi altro (o meglio, cercare di produrre codice che aggiunga il minimo indispensabile a ciò che può essere fatto attraverso questi sull'oggetto relativo)?
    Impostando in questo senso il lavoro di riflesso gli oggetti che si vanno a scrivere dovrebbero essere più generici.
    Mi riferisco ad esmpio al fatto che ti sei preoccupato solo della realizzazione della r.v. lasciando poi ai metodi del contenitor scelto il resto del lavoro.

    Grazie mille davvero, i prox esercizi saranno sull'overload di operator e sulle gerarchie, spero di inquadrare meglio il tuo codice che così a colpo d'occhio non mi sarebbe mai venuto in mente di scrivere...stiamo a vedere...Ciao shodan e grazie ancora.
    Ultima modifica di Toxotes; 30-01-2014 a 00:14

  5. #5
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Da quel che vedo quindi l'approccio... (etc)
    E' un processo che si impara per gradi. L'apprendista programmatore deve prima di tutto preoccuparsi che il suo codice non dia errori (specie a runtime), poi dovrebbe iniziare a chiedersi se esiste un approccio migliore (o più generico) per quello che sta facendo e se esiste lo adotta. E' probabile che mentre studi lo Stroustrup (e acquisendo nuove nozioni) ti guardi indietro e ti accorgi che quello che hai già scritto puoi riscriverlo in modo diverso e migliore (magari più efficente). Sono sicuro che finito il libro avresti scritto il codice che ho scritto io.

    mi confermi che ti sembra buona norma utilizzare solo gli iteratori, functors o metodi di una determinata interfaccia standard per "smanettarci su" senza inventarsi altro (o meglio, cercare di produrre codice che aggiunga il minimo indispensabile a ciò che può essere fatto attraverso questi sull'oggetto relativo)?
    Nei limiti del possibile: si. Diciamo che meno codice si scrive, meno probabilità ci sono che tale codice non funzioni.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  6. #6
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    41
    @Shodan: stavo ragionando sulla tua implementazione (ma anche sulla mia) e mi è venuto da pensare quanto segue (chiedo a shodan perchè mi ha gentilmente aiutato, ma ovviamente qualsiasi commento è benvenuto):

    tu praticamente (come io d'altronde) hai creato una classe che costruttori e getters a parte, una volta istanziata, fornisce il metodo UniformDist() grazie all'overload di (). Ma pensavo: che senso ha creare una classe che ha come membro significativo una sola funzione di cui appunto poi devo fare l'overload () per richiamarla. Aldilà dell'aspetto didattico dell'esercizio ho pensato che i function object abbiano tutt'altro scopo o no?
    Cioè in questo caso io farei direttamente una funzione template, perchè fare questo casino per un solo metodo?

    Inoltre ti chiedo la seguente: non riesco bene ad inquadrare la differenza fra avere un functor e utilizzare un metodo di una classe. Il functor di fatto viene richiamato dopo che la classe viene istanziata giusto? Quindi diciamo che ho la class F e istanzio f, dopodichè da qualche parte utilizzerò f().
    Ma non è equivalente a avere class C che ha un metodo f e da qualche parte utlizzare C.f() ?

    Puoi spiegarmi cosa mi sfugge. Credo di aver capito qualcosa riguardo al cambiare o meno stato alla classe, ma ho davvero difficoltà a capire cosa significhi.

    Thx per la patience, ciao.
    Ultima modifica di Toxotes; 01-02-2014 a 00:30

  7. #7
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    fornisce il metodo UniformDist() grazie all'overload di ()
    Ma pensavo: che senso ha creare una classe che ha come membro significativo una sola funzione di cui appunto poi devo fare l'overload () per richiamarla.
    Attenzione che UniformDist() è il costruttore (più o meno parametrico), non un metodo. Io non faccio altro che istanziare la classe e passarne la copia a std::generate(). L'overload di operator() serve perché è richiesto dall'algoritmo usato da std::generate().
    http://en.cppreference.com/w/cpp/algorithm/generate
    Altri algoritmi hanno operator() diverso.
    http://en.cppreference.com/w/cpp/algorithm
    ho pensato che i function object abbiano tutt'altro scopo o no?
    No. Lo scopo di un function object è di poter trattare un oggetto come una funzione.
    Cioè in questo caso io farei direttamente una funzione template, perchè fare questo casino per un solo metodo?
    Nulla ti vieta di farlo, ma le variabili _a e _b devi passarle come parametri del template. Ricorda che la funzione non deve accettare parametri (nemmeno template) per essere usata con std::generate(). Se preferisci utilizzare un ciclo allora puoi utilizzare una funzione normale senza scomodare i template.
    In altre parole, gli algoritmi usati da STL richiedono determinate cose e se vuoi usarli devi implementare queste cose come vogliono loro. Ne più ne meno di qualsiasi altra funzione di libreria.

    Inoltre ti chiedo la seguente: non riesco bene ad inquadrare la differenza fra avere un functor e utilizzare un metodo di una classe. Il functor di fatto viene richiamato dopo che la classe viene istanziata giusto? Quindi diciamo che ho la class F e istanzio f, dopodichè da qualche parte utilizzerò f().
    Esatto.
    Ma non è equivalente a avere class C che ha un metodo f e da qualche parte utlizzare C.f() ?
    Se una funzione richiede C() quella gli devi dare. Chiariamoci: io ho usato operator() per rendere compatibile la classe UniformDist con l'algoritmo std::generate(), ma non c'è scritto da nessuna parte che debba essere l'unica soluzione: è solo una soluzione.
    Credo di aver capito qualcosa riguardo al cambiare o meno stato alla classe, ma ho davvero difficoltà a capire cosa significhi
    Guarda che operator() è una funzione membro come lo è C::f(), per cui ha accesso a tutte le variabili membro di C esattamente come C::f(). Non capisco cosa non ti sia chiaro.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  8. #8
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    41
    Nulla ti vieta di farlo, ma le variabili _a e _b devi passarle come parametri del template. Ricorda che la funzione non deve accettare parametri (nemmeno template) per essere usata con std::generate(). Se preferisci utilizzare un ciclo allora puoi utilizzare una funzione normale senza scomodare i template.
    In altre parole, gli algoritmi usati da STL richiedono determinate cose e se vuoi usarli devi implementare queste cose come vogliono loro. Ne più ne meno di qualsiasi altra funzione di libreria.
    Questo mi chiarisce molte cose. Grazie shodan.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.