View Full Version : STL: Farsi un fuctor iota sofisticato(Cionci ,Marco invited)
Mi sono accorto che la STL non ha nello standard il comodo functor iota implementato dalla SGI
(serve a generare sequenze di numeri in container tipo vector,list etc)
quindi me ne sono fatto uno a mano.(Puo darsi che nella libreria boost ci sia già, ma sulla macchina di mio fratello
non l'avevo a disposizione).
Non esitate a proporre miglioramenti o segnalare anomalie (ve ne sarei grato) :)
template <class T>
class iota_gen {
public:
iota_gen(T start,T increment) : current(start) ,inc(increment) {}
T operator () () {T tmp= current;
current+=inc;
return tmp ;}
private:
T current;
T inc;
};
//Esempio di chiamata
...
vector <double> viota(10);
//riempie viota a partire da 0.0 aggiungendo 1.5 al precedente elemento
generate(viota.begin(),viota.end(), iota_gen<double>(0.0,1.5));
...
Lo sfizio ora sarebbe farne una versione che permette l'uso dei functor standard
(sottrazione,moltiplicazione,etc)per scegliere il modo in cui generare la sequenza e
non limitarlo all'addizione.
Siete capaci di farlo?Io al momento no. :(
Non è un fatto necessario per me (al momento) ,ma mi sembrava un quesito interessante. ;)
Un esempio d'uso?
suponete di avere la funzione x=f(y) non invertibile (o anche non facilmente invertibile).
Voi avete bisogno invece di calcolare y=f(x) in un certo intervallo Xa -Xb .
Un modo semplice è quello di approssimare y=f(x) con una poligonale.
Quindi generate un vettore di n coordinate yi a partire da Ya con un intervallo (Ya-Yb)/(n-1);
un altro vettore di n coordinate xi=f(yi).
A questo punto potete calcolare qualunque valore Yc(Xc) sfruttando i punti a disposizione.
Questo è il caso reale che è capitato a me :)
Originally posted by "verloc"
Lo sfizio ora sarebbe farne una versione che permette l'uso dei functor standard
(sottrazione,moltiplicazione,etc)per scegliere il modo in cui generare la sequenza e
non limitarlo all'addizione.
Potresti passargli un puntatore ad una funzione che prende 2 parametri...il valore corrente e quello dell'incremento...
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template <class T>
class iota_gen {
public:
iota_gen(T start,T increment, T (*oper)(T cur, T inc) = default_op)
: current(start) ,inc(increment), op(oper) {}
T operator () () {T tmp = current;
current = op(current, inc);
return tmp;};
protected:
virtual T default_op(T cur, T incr) {return cur+incr;};
private:
T current;
T inc;
T (*op)(T st, T inc);
};
template <class T>
T molt(T current, T increment)
{
return current * increment;
}
vector <double> viota(10);
...
generate(viota.begin(),viota.end(), iota_gen<double>(0.1,1.5,molt));
...
Grazie Cionci ;)
avevo avuto l'idea dei puntatori ma non sapevo esattamente come farla.
la tua è una ottima idea.
La esamino meglio adesso.
Kleidemos
04-05-2003, 13:33
Originally posted by "cionci"
//....
T (*op)(T st, T inc);
//..
Mi spieghi come funz??
E un puntatore a funzione vero?
ma che cavolo di programma stai scrivendo??? :D
Originally posted by "Kleidemos"
Mi spieghi come funz??
E un puntatore a funzione vero?
I puntatori a funzioni hanno tipizzazione molto forte... Non solo è significativo il tipo di ritorno (T in questo caso), ma anche i parametri stessi...
T (*op)(T st, T inc);
T è il valore di ritorno...op è il nome del puntatore (c'è * davanti perchè è un puntatore)...il resto sono i parametri...
Non importa specificare i nome dei parametri formali (anzi non ha molto senso specificarli)... Meglio sarebbe scrivere così (anche io era da molto che non li usavo): T (*op)(T, T);
In pratica op può accettare solo funzioni il cui tipo di ritorno è T e i parametri sono 2 di entrambi di tipo T...
Dunque cionci,ieri ho fatto delle prove e
il compilatore mi segnala problemi solo quando lo chiamo senza specificare la funzione:
generate(viota.begin(),viota.end(), iota_gen<double>(1.0 , 2.0));
(non so;dovrebbe per come ho capito,richiamare default_op ma mi segnala questo:
Error E2034 riempvec.cpp 63: Cannot convert 'double (iota_gen<double>::*)(double,double)' to 'double (*)(double,double)' in function main()
Error E2151 riempvec.cpp 63: Type mismatch in default value for parameter 'oper' in function main() )
Comunque le tue indicazioni sono state preziosissime e ti spiego perchè.
1)Ho capito che l'operazione da usare doveva essere memorizzata nella classe.
2)Volevo usare i functor standard e dopo una mezz'ora di tentativi assurdi in cui
dichiaravo l'operazione come binary_function (non ti dico che usciva!)
3)Mi sono fermato a pensare,e ho concluso che plus divides etc in fondo non erano che degli
oggetti(la cosa non mi era proprio chiarissima) quindi bisognava dichiararli e usarli (un'altra mezz'ora per concludere che
dovevo metterli nel template :( )
template <typename T ,typename TFOO>
class iota_gen3 {
public:
iota_gen3(T start,T increment,TFOO tfo )
: current(start) ,inc(increment) ,foo(tfo) {}
T operator () () {T tmp = current;
current = foo(current, inc);
return tmp;};
private:
T current;
T inc;
TFOO foo;
};
In seguito solo dopo una grandisima botta di **** sono riuscito a fare la chiamata giusta
(perchè quando si tratta di template me ne vado subito e sempre nel pallone)
//Esempi di chiamata
generate(viota.begin(),viota.end(), iota_gen3<double ,multiplies<double> >(1.0,2.0,multiplies<double>()));
generate(viota.begin(),viota.end(), iota_gen3<double ,minus<double> >(10.0,1.0,minus<double>()));
Credimi ,quando ho visto che funzionava neanche io ci credevo :eek: :eek: :eek:
Vedi un pò tu se si può aggiustare qualcosa (la chiamata non è un esempio di leggibilità :D ).
Comunque grazie per avermi fatto vedere come si usa un puntatore a funzione in una classe.
Credo che l' 80 % del merito di iota_gen3 sia tuo. ;)
(Quanno ce vò ce vò :D )
A proposito:
Chiamare una funzione tramite puntatore(a funzione) è + veloce rispetto ad una normale?
Ps.scusate il papiello :)
Originally posted by "misterx"
ma che cavolo di programma stai scrivendo??? :D
seh...ridi tu.
Sto scrivendo la mia tesi,questo codice mi è servito realmente(leggi sopra
a cosa).
La morale di verloc sulla STL:
quando vedi per la prima certe funzioni,tu dici :
"Daiiiii ... a chi puo fregare di queste str*****e ?"
Poi arrivi ad incocciarle :) e allora scopri la loro potenza...
"Luke...non sottovalutare il lato oscuro della STL"
:)
Bene :)
Riguardo alla velocità... Direi proprio di no... Visto che bisogna andare ad accedere in memoria per recuperare l'indirizzo della funzione che altrimenti sarebbe calcolato automaticamente in fase di compilazione...
/\/\@®¢Ø
06-05-2003, 10:40
Vedo che avete gia' fatto tutto voi :D
Solo un paio di piccoli suggerimenti per la leggibilita'.
La prima e' quella di aggiungere un parametro di default per l'oggetto da utilizzare,
template <typename T ,typename TFOO>
class iota_gen3 {
public:
iota_gen3(T start,T increment,TFOO tfo [b]=TFOO()[/b] )
: current(start) ,inc(increment) ,foo(tfo) {}
Nei casi semplici questo accorcia la chiamata a
//Esempi di chiamata
generate(viota.begin(),viota.end(), iota_gen3<double ,multiplies<double> >(1.0,2.0));
generate(viota.begin(),viota.end(), iota_gen3<double ,minus<double> >(10.0,1.0));
Per i casi "complessi" (dove l'oggetto va costruito con parametri), io aggiungerei la seguente funzione:
template <typename T , typename TFOO>
iota_gen3<T,TFOO> mk_iota( T x , T y , TFOO z )
{
return iota_gen3<T,TFOO>(x,y,z);
}
Con questo puoi evitare un po- di parametri template, visto che verranno identificati automaticamente:
generate(viota.begin(),viota.end(), mk_iota(1.0,2.0, multiplies<double>() ));
o meglio ancora
multiplies<double> mul;
generate(viota.begin(),viota.end(), mk_iota(1.0,2.0,mul ));
Semplicemente Meraviglioso,Marco!
Non scherzo se dico che tu e Cionci date delle botte di progresso enormi
alle mie conoscenze di apprendista.
Sul serio,credetimi!
E' riconoscenza,non piaggeria!
Mercì :)
E' /\/\@®¢Ø il mago della STL ;) Io sono ancora un principiante :)
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.