首先,假定有一个合适的二维矩阵类,但这不是问题。但是我不知道你的API,所以我会用典型的东西来说明:
struct coord {
size_t x; // x position or column count
size_t y; // y position or row count
};
template <typename T>
class Matrix2D {
⋮ // implementation details
public:
⋮ // all needed special members (ctors dtor, assignment)
Matrix2D (coord dimensions);
coord dimensions() const; // return height and width
const T& cell (coord position) const; // read-only access
T& cell (coord position); // read-write access
// handy synonym:
const T& operator[](coord position) const { return cell(position); }
T& operator[](coord position) { return cell(position); }
};
我刚刚展示了我需要的公共成员:创建一个给定大小的矩阵、查询大小以及对各个元素的索引访问。
因此,鉴于此,您的问题描述是:
template<typename T>
Matrix2D<T> scale_pow2 (const Matrix2D& input, size_t pow)
{
const auto scale_factor= 1 << pow;
const auto size_in = input.dimensions();
Matrix2D<T> result ({size_in.x*scale_factor,size_in.y*scale_factor});
⋮
⋮ // fill up result
⋮
return result;
}
好的,现在问题已经精确定义了:上面的大空白处是什么代码?
输入中的每个单元格都被放入输出中的一堆单元格中。因此,您可以遍历输入并在输出中写入一组具有相同值的单元格,或者您可以遍历输出并在输入中查找您需要其值的每个单元格。
后者更简单,因为您不需要嵌套循环(或一对循环)来编写丛集。
for (coord outpos : /* ?? every cell of the output ?? */) {
coord frompos {
outpos.x >> pow,
outpos.y >> pow };
result[outpos] = input[frompos];
}
现在很简单!
计算给定输出的 from 位置必须与定义比例的方式相匹配:您将有 pow 位给出相对于该块的位置,而较高的位将是该位置的索引丛来自
现在,我们要将outpos 设置为输出矩阵索引中的每个合法位置。这就是我需要的。如何真正做到这一点是另一个子问题,可以通过 自上而下的分解来解决。
更高级一点
也许嵌套循环是完成此任务的最简单方法,但我不会将它们直接放入此代码中,从而将我的嵌套级别推得更深。并且循环 0..max 不是用没有库的裸 C++ 编写的最简单的事情,所以这只会让人分心。而且,如果您正在使用矩阵,这是您普遍需要的东西,包括(比如说)打印出答案!
所以这是双循环,放入自己的代码中:
struct all_positions {
coord current {0,0};
coord end;
all_positions (coord end) : end{end} {}
bool next() {
if (++current.x < end.x) return true; // not reached the end yet
current.x = 0; // reset to the start of the row
if (++current.y < end.y) return true;
return false; // I don't have a valid position now.
}
};
这不遵循可以在基于范围的for 循环中使用的迭代器/集合 API。有关如何执行此操作的信息,请参阅 my article on Code Project 或使用 C++20 标准库中的 Ranges 内容。
鉴于这个“老式”迭代助手,我可以将循环编写为:
all_positions scanner {output.dimensions}; // starts at {0,0}
const auto& outpos= scanner.current;
do {
⋮
} while (scanner.next());
由于实现简单,从 {0,0} 开始,同时推进也进行测试,不能推进时返回false。因此,您必须声明它(给出第一个单元格),使用它,然后进行高级和测试。也就是说,一个测试在末端的循环。 C++ 中的for 循环在每次使用之前检查条件之前,并在最后使用不同的函数前进。因此,使其与for 循环兼容需要更多工作,而令人惊讶的是,使其与 ranged-for 兼容并没有更多工作。分离测试,以正确的方式推进,才是真正的工作;剩下的只是命名约定。
只要这是“自定义”,您可以根据需要进一步修改它。例如,在内部添加一个标志来告诉您该行何时更改,或者它是一行的第一行或最后一行,以便于进行漂亮的打印。
总结
除了您真正想要编写的一小段代码之外,您还需要大量工作。在这里,它是一个可用的 Matrix 类。很多时候,它会提示输入、打开文件、处理命令行选项等等。它分散了真正问题的注意力,所以首先解决这个问题。
将你的代码(你得到的真正代码)写在它自己的函数中,与你为了容纳它而需要的任何其他东西分开。如果可以的话,到别处去;这不是课程的一部分,只是分散注意力。更糟糕的是,这可能是您没有准备好(或做得好)的“困难”,因为它与正在学习的实际课程无关。
在将算法转换为您正在使用的对象的合法语法和 API 之前,以一般方式找出算法(流程图、伪代码等)。如果你只是在学习 C++,当你试图弄清楚逻辑时,不要陷入正式的语法中。除非您在进行此类规划时自然地开始考虑in C++,否则不要强迫它。使用白板涂鸦、修补玩具,任何适合你的东西。
在您花时间编码之前,从您的同行和导师(如果有)那里获得对想法的反馈和审查,以及如何实现它的逻辑。为什么要写一个行不通的想法?修复逻辑,而不是代码。
最后,画出你需要的控制流、函数和数据结构。使用伪代码和占位符注释。
然后填写占位符并将伪替换为合法语法。您已经计划好了,所以现在您可以专注于学习编程语言的语法和库细节。您可以专注于“我如何在 C++ 中表达(一些微小的细节)”,而不是将整个程序留在脑海中。更一般地说,隔离你将要学习的部分;学习/练习一件事,而不用担心整个大厦。
在很大程度上,其中一些想法也会转化为代码。 自上而下的设计意味着您在高层次上陈述事物,然后在别处单独实施。它使代码具有可读性和可维护性,并且首先更容易编写。函数应该这样写:函数解释了如何做(它做了什么)作为一个细节列表,这些细节只是更进一步的细节层次。然后,这些步骤中的每一个都成为一个新功能。函数应该简短,并在一个抽象的语义级别上表达。 不要深入了解函数中最原始的细节,这些细节将任务解释为一组更简单的步骤。
祝你好运,继续努力!