广义外积 - 函数接受 lambda
Generalized Outer Product - function accepts lambda
我正在尝试创建一个函数,该函数将以创建动态分配的二维数组(矩阵)的方式生成两个数组的广义外积。
前两个参数的类型必须与后两个参数的类型相同,但前两个参数的类型不必与后两个参数的类型相同(例如前两个参数可以是指针到整数组)。这也意味着这两个数组的元素不一定必须是同一类型。至于 function ,它应该是一个 lambda 函数。唯一的限制是它必须能够接收这两个数组的元素作为参数。函数返回值的类型可以是任意类型,该类型将是矩阵的元素。
例子
5 2 8
1 3 6 2
函数 f(x,y)=x+2y
7 11 17 9
4 8 14 6
10 14 20 12
#include <iostream>
#include <vector>
/*I don't know how to make Generalized_Outer_Product accept lambda function*/
template < typename iterator_tip1, typename iterator_tip2, typename tip >
auto Generalized_Outer_Product(iterator_tip1 start_first, iterator_tip1 after_end_first,
iterator_tip2 start_second, iterator_tip2 after_end_second, tip f(tip)) {
using type_of_object_first = typename std::decay < decltype( * start_first) > ::type;
using type_of_object_second = typename std::decay < decltype( * start_second) > ::type;
int n1 = std::distance(start_first, after_end_first);
int n2 = std::distance(start_second, after_end_second);
type_of_object_first ** mat = nullptr;
mat = new int * [n1];
for (int i = 0; i < n1; i++)
mat[i] = nullptr;
try {
for (int i = 0; i < n1; i++)
mat[i] = new type_of_object_first[n2];
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++) {
mat[i][j] = f( * start_first, * start_second);
start_second++;
}
start_first++;
start_second -= n2;
}
} catch (...) {
for (int i = 0; i < n1; i++)
delete[] mat[i];
delete[] mat;
throw std::range_error("Not enough memory");
}
return mat;
}
int main() {
int n1, n2, x;
std::cin >> n1;
std::vector < int > a, b, c;
for (int i = 0; i < n1; i++) {
std::cin >> x;
a.push_back(x);
}
std::cin >> n2;
for (int i = 0; i < n2; i++) {
std::cin >> x;
b.push_back(x);
}
try {
std::cout << "Generalized Outer Product f(x,y)=x+2y:" << std::endl;
int ** mat = nullptr;
mat = Generalized_Outer_Product(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) {
return x + 2 * y;
});
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++)
std::cout << mat[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < n1; i++)
delete[] mat[i];
delete[] mat;
} catch (std::range_error e) {
std::cout << e.what();
}
return 0;
}
你能帮我正确接受lambda函数吗?
TL DR
只保留参数类型开放,让编译器处理无效的参数类型;使用decltype
判断结果元素类型:
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
using ResultType = decltype(f(*start1, *start2));
...
}
其他建议 + 完整代码
首先为自己创建一个管理数据生命周期的类型。这简化了异常处理。如果操作正确,您可以简单地在 GeneralizedProduct
函数中不处理您的异常,而不必担心内存泄漏。
下面的class只是简单的用一个std::vector
来存储一行一行的数据。索引运算符利用向量迭代器允许我们编写 iter[index]
来获取对迭代器位置之后的元素 index
元素的引用。
template<class T>
struct MultiplicationResult
{
public:
using ValueType = T;
using IndexOperatorElement = std::vector<ValueType>::iterator;
using IndexOperatorElementConst = std::vector<ValueType>::const_iterator;
MultiplicationResult(size_t dimension1, std::vector<T>&& data)
: m_dimension1(dimension1)
{
if (data.size() % dimension1 != 0)
{
throw std::runtime_error("invalid data: the number of data elements is not divisible by dimension1");
}
m_dimension2 = data.size() / dimension1;
m_data = std::move(data);
}
IndexOperatorElement operator[](size_t index)
{
return m_data.begin() + index * m_dimension2;
}
IndexOperatorElementConst operator[](size_t index) const noexcept
{
return m_data.cbegin() + index * m_dimension2;
}
size_t Dimension1() const noexcept
{
return m_dimension1;
}
size_t Dimension2() const noexcept
{
return m_dimension2;
}
private:
std::vector<T> m_data;
size_t m_dimension1;
size_t m_dimension2;
};
现在 GeneralizedProduct
函数要做的就是确定结果类型并创建将用作 MultiplicationResult
.
数据的向量
请注意,我们不以任何方式限制此处传递的函数的类型。如果给定取消引用的迭代器无法调用传入参数的调用运算符,或者如果结果类型不是可以用作 std::vector
的模板参数的类型,则编译将导致错误。请注意,lambda 是具有调用运算符的对象,因此不能分配给 result_type(argument_type1, argument_type2)
。 (我认为这种方法比使用 std::function<result_type(argument_type1, argument_type2)>
:
更方便
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
我们简单地使用 decltype
:
来确定调用调用运算符的结果类型
using ResultType = decltype(f(*start1, *start2));
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
using ResultType = decltype(f(*start1, *start2));
size_t const n1 = std::distance(start1, end1);
// if an exception happens, data will ensure the elements already allocated get destroyed
std::vector<ResultType> data;
data.reserve(n1 * std::distance(start2, end2));
for (Iterator1 pos1 = start1; pos1 != end1; ++pos1)
{
for (Iterator2 pos2 = start2; pos2 != end2; ++pos2)
{
data.push_back(f(*pos1, *pos2));
}
}
return MultiplicationResult(n1, std::move(data)); // C++17 CTAD deduces the template arguments here
}
示例用法(无异常处理:
int main() {
std::vector<int> a{ 5, 2, 8 };
std::vector<int> b{ 1, 3, 6, 2 };
auto result = GeneralizedOuterProduct(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) { return x + 2* y; });
size_t const d1 = result.Dimension1();
size_t const d2 = result.Dimension2();
for (size_t i = 0; i != d1; ++i)
{
auto innerArray = result[i];
for (size_t j = 0; j != d2; ++j)
{
std::cout << std::setw(5) << innerArray[j];
}
std::cout << '\n';
}
}
我正在尝试创建一个函数,该函数将以创建动态分配的二维数组(矩阵)的方式生成两个数组的广义外积。 前两个参数的类型必须与后两个参数的类型相同,但前两个参数的类型不必与后两个参数的类型相同(例如前两个参数可以是指针到整数组)。这也意味着这两个数组的元素不一定必须是同一类型。至于 function ,它应该是一个 lambda 函数。唯一的限制是它必须能够接收这两个数组的元素作为参数。函数返回值的类型可以是任意类型,该类型将是矩阵的元素。
例子
5 2 8
1 3 6 2
函数 f(x,y)=x+2y
7 11 17 9
4 8 14 6
10 14 20 12
#include <iostream>
#include <vector>
/*I don't know how to make Generalized_Outer_Product accept lambda function*/
template < typename iterator_tip1, typename iterator_tip2, typename tip >
auto Generalized_Outer_Product(iterator_tip1 start_first, iterator_tip1 after_end_first,
iterator_tip2 start_second, iterator_tip2 after_end_second, tip f(tip)) {
using type_of_object_first = typename std::decay < decltype( * start_first) > ::type;
using type_of_object_second = typename std::decay < decltype( * start_second) > ::type;
int n1 = std::distance(start_first, after_end_first);
int n2 = std::distance(start_second, after_end_second);
type_of_object_first ** mat = nullptr;
mat = new int * [n1];
for (int i = 0; i < n1; i++)
mat[i] = nullptr;
try {
for (int i = 0; i < n1; i++)
mat[i] = new type_of_object_first[n2];
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++) {
mat[i][j] = f( * start_first, * start_second);
start_second++;
}
start_first++;
start_second -= n2;
}
} catch (...) {
for (int i = 0; i < n1; i++)
delete[] mat[i];
delete[] mat;
throw std::range_error("Not enough memory");
}
return mat;
}
int main() {
int n1, n2, x;
std::cin >> n1;
std::vector < int > a, b, c;
for (int i = 0; i < n1; i++) {
std::cin >> x;
a.push_back(x);
}
std::cin >> n2;
for (int i = 0; i < n2; i++) {
std::cin >> x;
b.push_back(x);
}
try {
std::cout << "Generalized Outer Product f(x,y)=x+2y:" << std::endl;
int ** mat = nullptr;
mat = Generalized_Outer_Product(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) {
return x + 2 * y;
});
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++)
std::cout << mat[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < n1; i++)
delete[] mat[i];
delete[] mat;
} catch (std::range_error e) {
std::cout << e.what();
}
return 0;
}
你能帮我正确接受lambda函数吗?
TL DR
只保留参数类型开放,让编译器处理无效的参数类型;使用decltype
判断结果元素类型:
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
using ResultType = decltype(f(*start1, *start2));
...
}
其他建议 + 完整代码
首先为自己创建一个管理数据生命周期的类型。这简化了异常处理。如果操作正确,您可以简单地在 GeneralizedProduct
函数中不处理您的异常,而不必担心内存泄漏。
下面的class只是简单的用一个std::vector
来存储一行一行的数据。索引运算符利用向量迭代器允许我们编写 iter[index]
来获取对迭代器位置之后的元素 index
元素的引用。
template<class T>
struct MultiplicationResult
{
public:
using ValueType = T;
using IndexOperatorElement = std::vector<ValueType>::iterator;
using IndexOperatorElementConst = std::vector<ValueType>::const_iterator;
MultiplicationResult(size_t dimension1, std::vector<T>&& data)
: m_dimension1(dimension1)
{
if (data.size() % dimension1 != 0)
{
throw std::runtime_error("invalid data: the number of data elements is not divisible by dimension1");
}
m_dimension2 = data.size() / dimension1;
m_data = std::move(data);
}
IndexOperatorElement operator[](size_t index)
{
return m_data.begin() + index * m_dimension2;
}
IndexOperatorElementConst operator[](size_t index) const noexcept
{
return m_data.cbegin() + index * m_dimension2;
}
size_t Dimension1() const noexcept
{
return m_dimension1;
}
size_t Dimension2() const noexcept
{
return m_dimension2;
}
private:
std::vector<T> m_data;
size_t m_dimension1;
size_t m_dimension2;
};
现在 GeneralizedProduct
函数要做的就是确定结果类型并创建将用作 MultiplicationResult
.
请注意,我们不以任何方式限制此处传递的函数的类型。如果给定取消引用的迭代器无法调用传入参数的调用运算符,或者如果结果类型不是可以用作 std::vector
的模板参数的类型,则编译将导致错误。请注意,lambda 是具有调用运算符的对象,因此不能分配给 result_type(argument_type1, argument_type2)
。 (我认为这种方法比使用 std::function<result_type(argument_type1, argument_type2)>
:
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
我们简单地使用 decltype
:
using ResultType = decltype(f(*start1, *start2));
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
using ResultType = decltype(f(*start1, *start2));
size_t const n1 = std::distance(start1, end1);
// if an exception happens, data will ensure the elements already allocated get destroyed
std::vector<ResultType> data;
data.reserve(n1 * std::distance(start2, end2));
for (Iterator1 pos1 = start1; pos1 != end1; ++pos1)
{
for (Iterator2 pos2 = start2; pos2 != end2; ++pos2)
{
data.push_back(f(*pos1, *pos2));
}
}
return MultiplicationResult(n1, std::move(data)); // C++17 CTAD deduces the template arguments here
}
示例用法(无异常处理:
int main() {
std::vector<int> a{ 5, 2, 8 };
std::vector<int> b{ 1, 3, 6, 2 };
auto result = GeneralizedOuterProduct(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) { return x + 2* y; });
size_t const d1 = result.Dimension1();
size_t const d2 = result.Dimension2();
for (size_t i = 0; i != d1; ++i)
{
auto innerArray = result[i];
for (size_t j = 0; j != d2; ++j)
{
std::cout << std::setw(5) << innerArray[j];
}
std::cout << '\n';
}
}