问题
I have a 1D eigen array (Eigen::Array<double,Dynamic,Dynamic>
) of doubles, and I want to modify each element in the array in place. However, I'm not really sure how to do this. I'm considering this:
Eigen::Array<double,Eigen::Dynamic,Eigen::Dynamic> arr1D;
// ...
// Threshold function:
arr1D.unaryExpr([](double& elem)
{
elem = elem < 0.0 ? 0.0 : 1.0;
}
);
But this seems like a bit of a hack because the Eigen Reference examples only give examples of .unaryExpr
where it is used with a functor that returns a value (and then the whole method just returns a different array). In my case, I was hoping to avoid the need for creating a new array.
I'm new to Eigen so I thought I might be missing something here, input is appreciated.
EDIT: I understand that I can replace the above simply with arr1D = arr1D >= 0.0
, but please note that this is just an example
回答1:
.unaryExpr
returns "view" to original data transformed by given function. It doesn't do transformation of original data.
You cannot change argument passed to transformation function. Your code is compiled only because you have not triggered template instantiation of appropriate code. If assign result to value then it fails to compile:
#include <Eigen/Dense>
int main()
{
using namespace Eigen;
ArrayXd x, y;
y = x.unaryExpr([](double& elem)
{
elem = elem < 0.0 ? 0.0 : 1.0;
}); // ERROR: cannot convert const double to double&
}
Exact place of error is in Eigen
internals:
EIGEN_STRONG_INLINE const Scalar coeff(Index index) const
{
return derived().functor()( derived().nestedExpression().coeff(index) );
// ^^^^^^^^^^^^^^^^^^^ - your lambda
}
I think the easiest way to do in-place with Eigen
is:
ArrayXd x = ArrayXd::Random(100);
x = x.unaryExpr([](double elem) // changed type of parameter
{
return elem < 0.0 ? 0.0 : 1.0; // return instead of assignment
});
unaryExpr
does not return full new array/matrix - but it returns special temporary object which acts like it.
回答2:
Evgeny's answer is by far the best if your value types are relatively simple.
However, if you want to recreate real()
and complex()
(accessing portions of the Scalar
s structure), you can use const_cast<>
to hack it all away, which is what real/complex()
actually do (as of v3.3.3):
(note: This code was only tested in C++1y, but could be simplified.)
struct Value {
double a{1.5};
int b{2};
};
// Savage. Use aforemention hack from scalar_real_ref_op.
struct get_mutable_a_direct {
double& operator()(const Value& v) const {
return const_cast<Value&>(v).a;
}
};
// ...
MatrixX<Value> X(2, 2);
auto X_am_direct = CwiseUnaryView<get_mutable_a_direct, MatrixX<Value>>(
X, get_mutable_a_direct());
X_am_direct.setConstant(20);
I've also tested out a quick wrapper, to reduce the above to something like:
struct get_a_flex {
double& operator()(Value& v) const {
return v.a;
}
const double& operator()(const Value& v) const {
return v.a;
}
};
// Less? savage.
auto X_am = unaryExprFlex(X, get_a_flex());
X_am *= 10;
cout << X_ac << endl;
// Works.
const auto& Xc = X;
auto Xc_am = unaryExprFlex(Xc, get_a_flex());
// Xc_am.setConstant(20); // Fails as desired.
cout << Xc_am << endl;
You can see the code snippet here: unary_view_mutable.cc
NOTE: If you want to use labmda, be sure you indicate returning a reference via auto&
:
auto X_bm = unaryExprFlex(X, [](Value& v) -> auto& { return v.b; });
cout << X_bm << endl;
X_bm.setConstant(10);
cout << X_bm << endl;
来源:https://stackoverflow.com/questions/19624632/how-do-i-in-place-modify-each-element-of-a-1d-array