Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

Έστω οτι έχουμε 3 συναρτήσεις :

float Const(float _x)
{
   return _x;
}
 
float Linear(float _coef,float _x)
{
   return _coef * _x;
}
 
float Env(<fp>,int _x)
{
   for(..for each _x..)
   {
      // call <fp>()
   }
}

Γίνεται να περνώ σαν όρισμα στην Env() διαφορετικές συναρτήσεις αλλά που όλες επιστρέφουν τον ίδιο τύπο έτσι ώστε να τις καλώ μέσα στην Env() ?

Δημοσ. (επεξεργασμένο)

typedef float (*float_func_type)(float);
typedef float (*float_func_type2)(float,float);

void invoker(float_func_type func, float arg)
{
   std::cout<<func(arg);
}
void invoker(float_func_type2 func, float arg1,float arg2)
{
   std::cout<<func(arg1,arg2);
}

float p1(float i)
{
   return i+1.0f;
}

float p2(float i,float j)
{
   return i*j;
}

int main(void)
{
   invoker(p1,10.f);
   invoker(p2,10.f,5.0f);
   sf::sleep(sf::seconds(10.0f));
   return 0;
}
Έτσι βγάζει αποτέλεσμα αλλά έχω 2 typedef και 2 εκδόσεις της invoker() .

 

 

EDIT: Φτιάχνω μία εικόνα να καταλάβετε τι προσπαθώ να κάνω . Ίσως χρειάζεται διαφορετική υλοποίηση .

 

 

EDIT2:

 

lpf.png

 

Και αν πχ ήθελα το lowpass να 'κόβει' μετα απο μία συγκεκριμένη συχνότητα ( ας πούμε 500Hz ) θα έγραφα :

 

LowPassFilter(&samples,Const(500.0));
ενώ αν ήθελα η συχνότητα να μειώνεται με το χρόνο θα έγραφα :

 

LowPassFilter(&samples,Linear(Const(-5.0f),...));
Το θέμα είναι πώς καλώ διαφορετικές συναρτήσεις προς τα πάνω οταν χρειάζεται αυτο ; Επεξ/σία από NewProject
Δημοσ.

Για να έχεις διαφορετικό πλήθος ορισμάτων σε κάθε συνάρτηση μάλλον αυτό που χρειάζεσαι είναι

functors, δηλαδή objects των οποίων οι κλάσεις τους κάνουν overload τον τελεστή (). Τα διαφορετικά

ορίσματα θα τα περνάς στον functor μέσω του constructor του.

 

π.χ

#include <algorithm>
#include <iostream>

class Sum {

    int& total;

    int power;
	
    public:
  	
    Sum(int& total, int power) 
    : total(total),
      power(power) {
    }
	
    void operator()(int& current) {
        total += pow(current, power);
    }
	
    int getTotal() const {
        return total;
    }
	
    private:
	
    int pow(int base, unsigned exponent) {
	
        if(exponent == 0) {
            return 1;
        }    

        int ret = base;
	
        for(unsigned i = 1; i < exponent; i++) {
		
            ret *= base;
		
        }
		
        return ret;
	
    }
	
};


int main(int argc, char** argv) {

    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	
    int total = 0;
	
    Sum sum = std::for_each(array, array + 9, Sum(total, 2));
	
    std::cout << sum.getTotal() << '\n';

    return 0;
}

Προφανώς στο συγκεκριμένο παράδειγμα μπορεί να χρησιμοποιηθεί η std::accumulate απλά ήταν ότι πιο απλό μπορούσα να σκεφτώ.

 

Παρατήρησε πως το field total στην κλάση είναι reference σε μια εξωτερική μεταβλητή καθώς το standard της C++ δε μας εγγυάται ότι η for_each για κάθε στοιχείο θα χρησιμοποιήσει το ίδιο object στο πέρασμα που θα κάνει, οπότε αν χρειάζεσαι ο functor σου να κρατά state πρέπει να γίνεται μέσω reference ή pointer σε κοινή μνήμη.

 

Υ.Γ Δε ξέρω αν παίζει κάποια μόντα σε C++11 που να απλοποιεί τα πράγματα, αν γνωρίζει κάποιος ας μας διαφωτίσει.

  • Like 1
  • Moderators
Δημοσ.

Υ.Γ Δε ξέρω αν παίζει κάποια μόντα σε C++11 που να απλοποιεί τα πράγματα, αν γνωρίζει κάποιος ας μας διαφωτίσει.

 

http://en.cppreference.com/w/cpp/utility/functional/function

http://oopscenities.net/2012/02/24/c11-stdfunction-and-stdbind/

 

Αυτά ίσως βοηθήσουν.

Δημοσ.

Μπα δε νομίζω ότι κάνουν αυτά τα links. O op χρειάζεται να περάσει κάποια extra ορίσματα εκτός από το όρισμα που περνάει η for_each το οποίο είναι το τρέχον element, τα οποία κιόλας να μην ορίζονται σε ΚΑΘΕ κλήση αλλά μόνο κατα την αρχικοποίηση. Τα links είναι χρήσιμα αν θες function pointer σε συναρτήσεις με συγκεκριμένο προτότυπο, 

Δημοσ.

Σας ευχαριστώ όλους για τις γνώμες και για τη βοήθεια.

Το βασικό πρόβλημα είναι οτι το loop αναγκαστικά πρέπει να είναι μέσα στη συνάρτηςη γιατί

πρίν απο αυτό προηγόυνται κάποιοι υπολογισμοί.Άν τη ξαναγράψω να κάνει process μόνο ένα στοιχείο

του πίνακα κάθε φορα και να τη καλώ ολόκληρη μεσα στη for το πρόγραμμα πατώνει.

Άν τώρα την αφήσω όπως είναι ναι μεν κάνει complile και τρέχει κανονικά αλλά οι τιμές δεν ενημερώνονται :

void LowPassFilter(float *samples,int size,float cutoff)
{
   // <- upologismoi ... polla CPU cycles
   for(i=0;i<size;i++)
   {
      process(samples[i],cutoff)
   }
}

float Linear(float coef,float time)
{
   return coef * time;
}


LowPassFilter(&samples,5000,Const(1000.0f));
//                              ^
//      otan kalw th LPF kaleitai h Const() kai h cutoff metavlhth
//      pairnei timh 1000 
//      OK !


LowPassFilter(&samples,5000,Linear(1000.0f,time));
//                              ^
//      otan kalw th LPF kaleitai h Linear kai pairnei timh 1000*time.
//      mesa sth for ths LPF omws den mporw na 3anakalesw th Linear me
//      orismata px (1000.0,i) gia na allazei h timh sto cutoff enw trexei
//      h for


Μία άλλη λύση θα ήταν όλες οι συναρτήσεις να έχουν ίδιες παραμέτρους , όσες έχει η μεγαλύτερη συνάρτηση και να τις καλώ

με τους δείκτες που πρότεινε Ο παπι αλλά μου φαίνεται λίγο άκομψο.

Δημοσ.

Όπως λέει ο Kercyn, το καλύτερο που μπορείς να κάνεις εδώ είναι να χρησιμοποιήσεις std::function και std::bind (υπόψιν ότι υπάρχουν αντίστοιχα εδώ και πάρα πολύ καιρό στην boost αν δεν έχεις C+11 compiler, πράγμα που βέβαια είναι απίθανο).

 

Τι θέλεις να κάνεις; Να περάσεις στην LPF μια συνάρτηση με όρισμα το χρόνο και αποτέλεσμα το cutoff frequency. Μια τέτοια συνάρτηση στη γενική περίπτωση που την αντιμετωπίζουμε σαν functor αντιπροσωπεύεται από τον τύπο

std::function<float(int)>

Σημειώνω ότι άλλαξα το όρισμα ("time") σε int από float γιατί από τον υπόλοιπο κώδικα φαίνεται ότι μιλάμε για samples και διακριτό χρόνο, οπότε τι νόημα έχει το float σ' αυτή την περίπτωση;

 

Αυτός είναι και ο τύπος του ορίσματος που θα περνάς στην LPF, δηλαδή:

void LowPassFilter(float *samples,int size,std::function<float(int)> cutoffProducer)
{
    for(i=0;i<size;i++)
    {
        process(samples[i],cutoffProducer(i));
    }
}

Οπότε τώρα μπορείς χρησιμοποιώντας lambdas να κάνεις αυτό:

LowPassFilter(samples, N, [](int) { return 1000; }); // const
LowPassFilter(samples, N, [](int i) { return 1000 * i; }); // linear

Και αν προτιμάς μπορείς να κρύψεις τις lambdas πίσω από όμορφα ονομασμένες factory functions:

std::function<float(int)> Const(float cutoff)
{
    return [=](int) { return cutoff; };
}

std::function<float(int)> Linear(float coefficient)
{
    return [=](int i) { return coefficient * i; };
}

LowPassFilter(samples, N, Linear(.5));

Όπως βλέπεις εδώ στο τέλος έχει γίνει αυτό ακριβώς που περιέγραψες αρχικά: οι Const και Linear έχουν το ίδιο return type.

 

Στον κώδικά σου έχει φανεί η ιδέα να ορίζεις τη γραμμική συνάρτηση με τη βοήθεια της σταθεράς, εκεί που λες Linear(Const(X), Y). Αυτό δεν είναι απαραίτητο και σίγουρα στην προκειμένη περίπτωση απλά προκαλεί μπέρδεμα χωρίς να προσφέρει τίποτα (γιατί να μην κάνεις τη linear να παίρνει κατευθείαν τα α και β της γραμμικής σχέσης και μπλέκεις την Const?). Το στυλάκι αυτό θα είχε νόημα μόνο αν οι συναρτήσεις αυτές έπαιρναν σαν όρισμα κάποιο τύπο με χαρακτηριστικά monad. Πράγμα που είναι πολύ χρήσιμο σαν τεχνική αλλά όχι εδώ.

 

Live στο ideone.

  • Like 1
Δημοσ.

Όπως λέει ο Kercyn, το καλύτερο που μπορείς να κάνεις εδώ είναι να χρησιμοποιήσεις std::function και std::bind (υπόψιν ότι υπάρχουν αντίστοιχα εδώ και πάρα πολύ καιρό στην boost αν δεν έχεις C+11 compiler, πράγμα που βέβαια είναι απίθανο).

 

Τι θέλεις να κάνεις; Να περάσεις στην LPF μια συνάρτηση με όρισμα το χρόνο και αποτέλεσμα το cutoff frequency. Μια τέτοια συνάρτηση στη γενική περίπτωση που την αντιμετωπίζουμε σαν functor αντιπροσωπεύεται από τον τύπο

std::function<float(int)>
Σημειώνω ότι άλλαξα το όρισμα ("time") σε int από float γιατί από τον υπόλοιπο κώδικα φαίνεται ότι μιλάμε για samples και διακριτό χρόνο, οπότε τι νόημα έχει το float σ' αυτή την περίπτωση;

Οπότε τώρα μπορείς χρησιμοποιώντας lambdas να κάνεις αυτό:

LowPassFilter(samples, N, [](int) { return 1000; }); // const
LowPassFilter(samples, N, [](int i) { return 1000 * i; }); // linear
Και αν προτιμάς μπορείς να κρύψεις τις lambdas πίσω από όμορφα ονομασμένες factory functions:

std::function<float(int)> Const(float cutoff)
{
    return [=](int) { return cutoff; };
}

std::function<float(int)> Linear(float coefficient)
{
    return [=](int i) { return coefficient * i; };
}

LowPassFilter(samples, N, Linear(.5));
Όπως βλέπεις εδώ στο τέλος έχει γίνει αυτό ακριβώς που περιέγραψες αρχικά: οι Const και Linear έχουν το ίδιο return type.

 

Αυτό ακριβώς ήθελα ναι ! Με λίγο διάβασμα από τα λίνκ νομίζω οτι θα γίνει οκ.

 

Στον κώδικά σου έχει φανεί η ιδέα να ορίζεις τη γραμμική συνάρτηση με τη βοήθεια της σταθεράς, εκεί που λες Linear(Const(X), Y). Αυτό δεν είναι απαραίτητο και σίγουρα στην προκειμένη περίπτωση απλά προκαλεί μπέρδεμα χωρίς να προσφέρει τίποτα (γιατί να μην κάνεις τη linear να παίρνει κατευθείαν τα α και β της γραμμικής σχέσης και μπλέκεις την Const?). Το στυλάκι αυτό θα είχε νόημα μόνο αν οι συναρτήσεις αυτές έπαιρναν σαν όρισμα κάποιο τύπο με χαρακτηριστικά monad. Πράγμα που είναι πολύ χρήσιμο σαν τεχνική αλλά όχι εδώ.

Το έγραψα για να δείξω στο απλοποιημένο παράδειγμα οτι μπορεί ναι έχουμε και άλλο ένα επίπεδο πρός τα πάνω ( δηλαδή οχι μόνο LFP(&s,0.5) ή LPF(&s,Linear(...)) αλλά και LPF(&s,Linear(Linear(Whatever(1.0))) )

 

Σας ευχαριστώ όλους για τη συμμετοχή !

Δημοσ.

Το έγραψα για να δείξω στο απλοποιημένο παράδειγμα οτι μπορεί ναι έχουμε και άλλο ένα επίπεδο πρός τα πάνω ( δηλαδή οχι μόνο LFP(&s,0.5) ή LPF(&s,Linear(...)) αλλά και LPF(&s,Linear(Linear(Whatever(1.0))) )

 

Ναι το κατάλαβα. Αν θέλεις να διαβάσεις περισσότερα για monads και να δεις γιατί ακριβώς το είπα αυτό, υπάρχει μια πολύ καλή σειρά blog posts για άτομα χωρίς ιδιαίτερο functional programming υπόβαθρο εδώ.

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...