[lambda] extending lambda functions
For some application I need to sum the complex modulus of a collection of elements. In short, I want to be able to replace the lambda expression _1 + bind(&std::norm<double>, _2) by something more natural like _1 + llnorm(_2) For that I use std::accumulate and the lambda library, like in the next working example; #include<boost/lambda/lambda.hpp> #include<boost/lambda/bind.hpp> using namespace boost::lambda; int main(){ using boost::lambda::_1; using boost::lambda::_2; std::vector<std::complex<double> > v(3); v[0]=1.; v[1]=2.; v[2]=3.; double result=std::accumulate(v.begin(), v.end(), double(0.), _1 + bind(&std::norm<double>, _2)); std::cout<<result<<std::endl; return 0; } the question is what do I have to do to be able to use the following syntax instead _1 + llnorm(_2) where llnorm is something I have to define (globally) somewhere else. The question is, how do I have to define llnorm to have the desired effect? The ideal syntax for me would be to be able to say std::norm(_2) or std::norm<double>(_2), but that may not be possible due to name conflict, is it? I am clueless because I don't even know even what is the (C++) type of the argument that would be taken by llnorm. Thanks, Alfredo
alfC wrote:
the question is what do I have to do to be able to use the following syntax instead _1 + llnorm(_2)
where llnorm is something I have to define (globally) somewhere else. The question is, how do I have to define llnorm to have the desired effect?
Lambda does not have such a facility. You can use Phoenix instead. Phoenix (lazy) functions allow you to do that. Phoenix is intended to supercede Lambda. Phoenix has been reviewed and is conditionally accepted into Boost after another mini-review. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net
Hi Joel, On Mar 21, 9:49 pm, Joel de Guzman <j...@boost-consulting.com> wrote:
alfC wrote:
the question is what do I have to do to be able to use the following syntax instead _1 + llnorm(_2)
Lambda does not have such a facility.
Ok, I believe you, I will use bind instead for the moment. However I am still puzzled: for example, when boost::lambda defines some operator+ (the one use above) it does indeed what we expect. What is special about operator+ compared to any other user defined (lazy) function defined by the user? (i.e. the lazy function that I want but don't know how to define in the context of Lambda) After all the operator+ above must be a function defined somewhere.
You can use Phoenix instead. Phoenix (lazy) functions allow you to do that. Phoenix is intended to supercede Lambda. Phoenix has been reviewed and is conditionally accepted into Boost after another mini-review.
I used Spirit a little bit and I recall that Phoenix was part of Spirit or something like that. While we wait for the Phoenix library, would you recommend to use the Phoenix included in Spirit? Thanks, Alfredo
alfC wrote:
Hi Joel,
On Mar 21, 9:49 pm, Joel de Guzman <j...@boost-consulting.com> wrote:
alfC wrote:
the question is what do I have to do to be able to use the following syntax instead _1 + llnorm(_2) Lambda does not have such a facility.
Ok, I believe you, I will use bind instead for the moment. However I am still puzzled: for example, when boost::lambda defines some operator+ (the one use above) it does indeed what we expect. What is special about operator+ compared to any other user defined (lazy) function defined by the user? (i.e. the lazy function that I want but don't know how to define in the context of Lambda) After all the operator+ above must be a function defined somewhere.
Because there is only a finite number of operators that we need to overload. OTOH, there's an infinite number of functions. In Phoenix, we take care of only a smallish subset related to STL algorithms and containers (e.g. for_each, etc.). It is easy to add more lazy standard functions if there are would be volunteers.
You can use Phoenix instead. Phoenix (lazy) functions allow you to do that. Phoenix is intended to supercede Lambda. Phoenix has been reviewed and is conditionally accepted into Boost after another mini-review.
I used Spirit a little bit and I recall that Phoenix was part of Spirit or something like that. While we wait for the Phoenix library, would you recommend to use the Phoenix included in Spirit?
Sure, why not? It is a very stable and mature library. Once we deal with the review related matters, there will be some changes, but I don't think it will be drastic. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net
Hi Joel,
the question is what do I have to do to be able to use the following syntax instead _1 + llnorm(_2)
Lambdadoes not have such a facility. You can use Phoenix instead. Phoenix (lazy) functions allow you to do that. Phoenix is intended to supercedeLambda. Phoenix has been reviewed and is conditionally accepted into Boost after another mini-review.
Ok, I gave up with Boost.Lambda for defining such "lazy function" and used Phoenix instead. The process was quite painless. Below is the (working) code to define a lazy complex modulus function for those who are interested. I have still two concerns though: 1) I am worried how the lazy function can figure out which version of std::norm<T> to call to obtain the right result. For std::complex<double> the lazy function works fine (double is hard coded). But for std::complex<int> it seems that the lazy function will always convert the result to double, I couldn't make the lazy funcion as generic as the original std::norm<T>(std::complex<T> const& c) function. Is it possible to improve the lazy function definition in that respect? (BTW, for std::complex, T == std::complex<T>::value_type) (It is not that I think std::complex<int> is useful for something but I thing it is a good example to ask about this). 2) I may be asking for too much sugar but is there a way to name the function "std::norm" and not have conflicts with the standard "std::norm<T>(std::complex<T> const& c)", the best I could do was to call it std::norm_; The following is the working example, compares the Lambda version with the Phoenix version (sum5 and sum6): #include<vector> #include<iostream> #include<boost/lambda/lambda.hpp> #include<boost/lambda/bind.hpp> #include<boost/spirit/phoenix.hpp> #include<numeric> //for accumulate #include<complex> using std::cout; using std::endl; using std::complex; using std::vector; double sum_norm(double acc, complex<double> elem){ return acc+std::norm(elem); } namespace std{ struct norm_impl{ template <typename Arg> struct result{ typedef double type; }; template <typename Arg> typename result<Arg>::type operator()(Arg ar1) const{ return std::norm(ar1); } }; phoenix::function<norm_impl> norm_; } int main(){ //create data unsigned l=9; vector<complex<double> > v(l); for(int i=0; i!=v.size(); ++i) v[i]=std::complex<double>(i,1.2+i); // using manual loop over array double sum1=0; for(int i=0; i!=v.size(); ++i) sum1+=norm(v[i]); cout<<"sum1 = "<<sum1<<endl; // using manual loop over container double sum2=0; for(vector<complex<double> >::const_iterator it=v.begin(); it!=v.end (); ++it) sum2+=norm(*it); cout<<"sum2 = "<<sum2<<endl; //using automatic loop (accumulate) and free function double sum3=std::accumulate(v.begin(), v.end(), double(0), sum_norm); cout<<"sum3 = "<<sum3<<endl; //using accumulate and lambda with pure bind using namespace boost::lambda; double sum4=std::accumulate(v.begin(), v.end(), double(0), bind( std::plus<double>(), _1, bind(&std::norm<double>, _2) ) ); cout<<"sum4 = "<<sum4<<endl; //using accumulate and lambda double sum5=std::accumulate(v.begin(), v.end(), double(0), _1 + bind(&std::norm<double>,_2)); cout<<"sum5 = "<<sum5<<endl; // (cout << _1 << " = " << _2 << "\n") ("fancy print sum5", sum5); //using accumulate and phoenix using namespace phoenix; double sum6=std::accumulate(v.begin(), v.end(), double(0), arg1 + std::norm_(arg2) ); cout<<"sum6 = "<<sum6<<endl; return 0; }
AMDG alfC wrote:
Ok, I gave up with Boost.Lambda for defining such "lazy function" and used Phoenix instead. The process was quite painless. Below is the (working) code to define a lazy complex modulus function for those who are interested. I have still two concerns though:
1) I am worried how the lazy function can figure out which version of std::norm<T> to call to obtain the right result. For std::complex<double> the lazy function works fine (double is hard coded). But for std::complex<int> it seems that the lazy function will always convert the result to double, I couldn't make the lazy funcion as generic as the original std::norm<T>(std::complex<T> const& c) function. Is it possible to improve the lazy function definition in that respect? (BTW, for std::complex, T == std::complex<T>::value_type)
result is a template, so you just need to specialize it or use some kind of metaprogramming (for instance Boost.Typeof).
(It is not that I think std::complex<int> is useful for something but I thing it is a good example to ask about this).
In fact, according to 26.2 "The effect of instantiating the template complex for any type other than float, double or long double is unspecified."
2) I may be asking for too much sugar but is there a way to name the function "std::norm" and not have conflicts with the standard "std::norm<T>(std::complex<T> const& c)", the best I could do was to call it std::norm_;
You're not allowed to put it in namespace std at all. In Christ, Steven Watanabe
1) I am worried how the lazy function can figure out which version of std::norm<T> to call to obtain the right result. For std::complex<double> the lazy function works fine (double is hard coded). But for std::complex<int> it seems that the lazy function will always convert the result to double, I couldn't make the lazy funcion as generic as the original std::norm<T>(std::complex<T> const& c) function. Is it possible to improve the lazy function definition in that respect? (BTW, for std::complex, T == std::complex<T>::value_type)
result is a template, so you just need to specialize it or use some kind of metaprogramming (for instance Boost.Typeof).
Do you mean that I should do something like (not tested):: template<class R> struct norm_impl{ template <typename Arg> struct result{ typedef R type; }; template <typename Arg> typename result<Arg>::type operator()(Arg ar1) const{ return std::norm(ar1); } }; template<class R> struct norm{ static(???) phoenix::function<norm_impl<R> > func; }; and use norm<double>::func (instead of norm_); How Boost.TypeOf fits in this or other approach?
(It is not that I think std::complex<int> is useful for something but I thing it is a good example to ask about this).
In fact, according to 26.2 "The effect of instantiating the template complex for any type other than float, double or long double is unspecified."
Good point, thank you. I should have asked for the std::complex<float> instead.
2) I may be asking for too much sugar but is there a way to name the function "std::norm" and not have conflicts with the standard "std::norm<T>(std::complex<T> const& c)", the best I could do was to call it std::norm_;
You're not allowed to put it in namespace std at all.
Obviously there is a mapping between standard functions and lazy functions (in Phoenix for example), Is there a way to express this mapping in their names? Thanks, Alfredo
AMDG alfC wrote:
result is a template, so you just need to specialize it or use some kind of metaprogramming (for instance Boost.Typeof).
Do you mean that I should do something like (not tested):: template<class R> struct norm_impl{ template <typename Arg> struct result{ typedef R type; }; template <typename Arg> typename result<Arg>::type operator()(Arg ar1) const{ return std::norm(ar1); } }; template<class R> struct norm{ static(???) phoenix::function<norm_impl<R> > func; }; and use norm<double>::func (instead of norm_);
How Boost.TypeOf fits in this or other approach?
No. I meant something like this: struct norm_impl { template<typename Arg> struct result { typedef Arg type; }; template<typename T> struct result<std::complex<T> > { typedef T type; }; //... }; Using Boost.Typeof result could be template<class Arg> struct result { BOOST_TYPEOF_NESTED_TYPEDEF_TPL(nested, (std::norm(Arg()))); typedef typename nested::type type; };
2) I may be asking for too much sugar but is there a way to name the function "std::norm" and not have conflicts with the standard "std::norm<T>(std::complex<T> const& c)", the best I could do was to call it std::norm_;
You're not allowed to put it in namespace std at all.
Obviously there is a mapping between standard functions and lazy functions (in Phoenix for example), Is there a way to express this mapping in their names?
Just put them in your own namespace. The ones defined by phoenix are in namespace boost::phoenix for instance. In Christ, Steven Watanabe
participants (3)
-
alfC
-
Joel de Guzman
-
Steven Watanabe