用不同的嵌套函数重构重复的循环代码
Refactoring repeated loop code with differing nested functions
为了图像处理目的,我有两个 for 循环遍历 2D 像素阵列。我想执行各种不同的方法,这些方法将改变对每个像素执行的功能。但是,for 循环的代码在这些方法中的每一个中都会重复。
有什么办法可以拔出来吗?
/**
* Get pixel array from current image.
*/
public void updatePixels() {
pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH];
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col ++) {
pixels[row][col] = image.getRGB(col, row);
}
}
}
/**
* Set pixel array to current image.
*/
public void commitPixels() {
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col ++) {
image.setRGB(col, row, pixels[row][col]);
}
}
}
public void greyscale() {
updatePixels();
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round((float)((r + g + b) / 3));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
}
}
commitPixels();
}
public void mirror() {
updatePixels();
int [][] pixelCopy = pixels.clone();
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
pixels[row][col] = pixelCopy[row][IMAGE_WIDTH - col - 1];
}
}
commitPixels();
}
从代码看来 updatePixels
和 commitPixel
可以移动到 greyscale
的 for
循环中
// updatePixel and commitPixel could be made inline as well.
public void updatePixel(int row, int col) {
pixels[row][col] = image.getRGB(col, row);
}
public void commitPixel(int row, int col) {
image.setRGB(col, row, pixels[row][col]);
}
public void computeColor(int row, int col) {
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round((float)((r + g + b) / 3));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
}
public void greyscale() {
pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH];
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
updatePixel(row, col);
computeColor(row, col);
commitPixel(row, col);
}
}
}
通过这种方法,您可以将 for 循环的执行次数从 3 次减少到 1 次。
由于循环是相同的,所以您可以用一种方法在 pxels
上执行所有操作
public void greyscale() {
pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH]; // updatePixels
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
pixels[row][col] = image.getRGB(col, row); // updatePixels
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round((float)((r + g + b) / 3));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
image.setRGB(col, row, pixels[row][col]); // commitPixels
}
}
}
您可以将循环隐藏在一个方法中,该方法采用 BiConsumer
作为获取当前列和行的参数:
private void forEachPixel(BiConsumer<Integer, Integer> consumer) {
for (int row = 0; row < pixels.length; row++) {
for (int col = 0; col < pixels[0].length; col++) {
consumer.accept(row, col);
}
}
}
现在,您可以为应该在嵌套循环内完成的操作编写 lambda:
public void updatePixels() {
forEachPixel((row, col) -> pixels[row][col] = image.getRGB(col, row));
}
public void commitPixels() {
forEachPixel((row, col) -> image.setRGB(col, row, pixels[row][col]));
}
public void greyscale() {
updatePixels();
forEachPixel((row, col) -> {
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round(((r + g + b) / 3f));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
});
commitPixels();
}
public void mirror() {
updatePixels();
int[][] pixelCopy = Arrays.stream(pixels).map(int[]::clone).toArray(int[][]::new);
forEachPixel((row, col) -> pixels[row][col] = pixelCopy[row][pixels[0].length - col - 1]);
commitPixels();
}
备注:在您的问题中,您在 mirror()
内的二维数组上使用 .clone()
。这会创建一个浅克隆,但您需要为后面的逻辑进行深克隆。这个问题在我的回答中得到解决。在 greyscale()
方法中,除法后不要强制转换为浮点数,而是将除数设为浮点数(参见我的回答)。
为了图像处理目的,我有两个 for 循环遍历 2D 像素阵列。我想执行各种不同的方法,这些方法将改变对每个像素执行的功能。但是,for 循环的代码在这些方法中的每一个中都会重复。
有什么办法可以拔出来吗?
/**
* Get pixel array from current image.
*/
public void updatePixels() {
pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH];
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col ++) {
pixels[row][col] = image.getRGB(col, row);
}
}
}
/**
* Set pixel array to current image.
*/
public void commitPixels() {
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col ++) {
image.setRGB(col, row, pixels[row][col]);
}
}
}
public void greyscale() {
updatePixels();
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round((float)((r + g + b) / 3));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
}
}
commitPixels();
}
public void mirror() {
updatePixels();
int [][] pixelCopy = pixels.clone();
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
pixels[row][col] = pixelCopy[row][IMAGE_WIDTH - col - 1];
}
}
commitPixels();
}
从代码看来 updatePixels
和 commitPixel
可以移动到 greyscale
for
循环中
// updatePixel and commitPixel could be made inline as well.
public void updatePixel(int row, int col) {
pixels[row][col] = image.getRGB(col, row);
}
public void commitPixel(int row, int col) {
image.setRGB(col, row, pixels[row][col]);
}
public void computeColor(int row, int col) {
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round((float)((r + g + b) / 3));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
}
public void greyscale() {
pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH];
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
updatePixel(row, col);
computeColor(row, col);
commitPixel(row, col);
}
}
}
通过这种方法,您可以将 for 循环的执行次数从 3 次减少到 1 次。
由于循环是相同的,所以您可以用一种方法在 pxels
上执行所有操作
public void greyscale() {
pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH]; // updatePixels
for (int row = 0; row < IMAGE_HEIGHT; row++) {
for (int col = 0; col < IMAGE_WIDTH; col++) {
pixels[row][col] = image.getRGB(col, row); // updatePixels
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round((float)((r + g + b) / 3));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
image.setRGB(col, row, pixels[row][col]); // commitPixels
}
}
}
您可以将循环隐藏在一个方法中,该方法采用 BiConsumer
作为获取当前列和行的参数:
private void forEachPixel(BiConsumer<Integer, Integer> consumer) {
for (int row = 0; row < pixels.length; row++) {
for (int col = 0; col < pixels[0].length; col++) {
consumer.accept(row, col);
}
}
}
现在,您可以为应该在嵌套循环内完成的操作编写 lambda:
public void updatePixels() {
forEachPixel((row, col) -> pixels[row][col] = image.getRGB(col, row));
}
public void commitPixels() {
forEachPixel((row, col) -> image.setRGB(col, row, pixels[row][col]));
}
public void greyscale() {
updatePixels();
forEachPixel((row, col) -> {
Color currentColour = new Color(pixels[row][col]);
int r = currentColour.getRed();
int g = currentColour.getGreen();
int b = currentColour.getBlue();
int avg = Math.round(((r + g + b) / 3f));
pixels[row][col] = new Color(avg, avg, avg).getRGB();
});
commitPixels();
}
public void mirror() {
updatePixels();
int[][] pixelCopy = Arrays.stream(pixels).map(int[]::clone).toArray(int[][]::new);
forEachPixel((row, col) -> pixels[row][col] = pixelCopy[row][pixels[0].length - col - 1]);
commitPixels();
}
备注:在您的问题中,您在 mirror()
内的二维数组上使用 .clone()
。这会创建一个浅克隆,但您需要为后面的逻辑进行深克隆。这个问题在我的回答中得到解决。在 greyscale()
方法中,除法后不要强制转换为浮点数,而是将除数设为浮点数(参见我的回答)。