我如何在 Rust 中简化这个可怕的嵌套错误处理代码?
How can I simplify this horribly nested error handling code in Rust?
我正在编写一个函数来计算 Rust 中 Vec<Vec<isize>>
的 Summed Area Table,而不使用任何外部板条箱。我正在努力学习尽可能地道地做到这一点,但我 运行 遇到了一些错误处理的障碍。
基本上我要做的就是这个,在维基百科页面上提到:
The summed-area table can be computed efficiently in a single pass
over the image, as the value in the summed-area table at (x, y) is
just:
=i(x,y)+I(x,y-1)+I(x-1,y)-I(x-1,y-1))
where i
provides values from the grid, and I
from the previously
computed values of the table. Obviously, if x or y is 0, then some of
these won't exist, in which case they are replaced with 0
.
然而,值得注意的是,如果 I(x, y - 1)
不存在,即使 y - 1
存在,那么我们正在使用的网格实际上是 non-rectangular,我们在那种情况下想要 return 一个 NonRectError
。
有了所有这些背景,这里是代码:我需要防止减法溢出错误,return 特殊情况下的 NonRectError
:
fn compute_summed_area_table(grid: &Vec<Vec<isize>>) -> Result<Vec<Vec<isize>>, NonRectError> {
let mut summed_area_table =
vec![Vec::with_capacity(grid[0].len()); grid.len()];
for (yi, row) in grid.iter().enumerate() {
for (xi, &value) in row.iter().enumerate() {
let (prev_row, prev_column_idx) = (
yi.checked_sub(1).and_then(|i| summed_area_table.get(i)),
xi.checked_sub(1)
);
let summed_values =
value +
// I(x, y - 1)
match prev_row {
None => &0,
Some(prev_row_vec) => match prev_row_vec.get(xi) {
Some(v) => v,
None => return Err(NonRectError { xi, yi })
}
} +
// I(x - 1, y)
(prev_column_idx
.and_then(|i| summed_area_table[yi].get(i))
.unwrap_or(&0)) -
// I(x - 1, y - 1)
(prev_row
.map_or(&0, |r| {
prev_column_idx
.and_then(|i| r.get(i))
.unwrap_or(&0)
}));
summed_area_table[yi].push(summed_values);
}
}
Ok(summed_area_table)
}
// Omitted the definition of NonRectError here, but it is defined.
这段代码显然是 sin 本身的定义,但我不确定从哪个角度来简化它 - 天哪,边缘情况太多了!
是否有任何 built-in 方法可以让我转义这种嵌套的 error-checking 内容?我可以用比这更简单的方式 return NonRectError
吗?
您可以尝试以下操作:
使用数组,而不是嵌套 Vec
s。使用数组,可以保证所有行的宽度都一样,不会出现NonRectError
。 (但也许你有充分的理由使用嵌套 Vec
s,所以我的其余示例使用嵌套 Vec
s。)
您计算 summed_value
的块很长。我会这样分解它:
// I(x, y - 1)
let north = ...;
// I(x - 1, y)
let west = ...;
// I(x - 1, y - 1)
let northwest = ...;
let summed_values = value + north + west - northwest;
而不是检查减法,检查 xi
和 yi
是否为非零更容易。此外,.ok_or()
是将 None
转换为错误的好方法。
let northwest = match (xi, yi) {
(0, _) => 0,
(_, 0) => 0,
(_, _) => {
// We know xi and yi are nonzero, so we can subtract without checks
summed_area_table.get(yi - 1)
.and_then(|row| row.get(xi - 1))
.ok_or(NonRectError { xi, yi })?
}
};
你也可以用 if/else
链来写。它们都是惯用的,这只是一个偏好问题。我比较喜欢match
,因为感觉更简洁
let northwest = if xi == 0 {
0
} else if yi == 0 {
0
} else {
// same as above
};
我正在编写一个函数来计算 Rust 中 Vec<Vec<isize>>
的 Summed Area Table,而不使用任何外部板条箱。我正在努力学习尽可能地道地做到这一点,但我 运行 遇到了一些错误处理的障碍。
基本上我要做的就是这个,在维基百科页面上提到:
The summed-area table can be computed efficiently in a single pass over the image, as the value in the summed-area table at (x, y) is just:
where
i
provides values from the grid, andI
from the previously computed values of the table. Obviously, if x or y is 0, then some of these won't exist, in which case they are replaced with0
.
然而,值得注意的是,如果 I(x, y - 1)
不存在,即使 y - 1
存在,那么我们正在使用的网格实际上是 non-rectangular,我们在那种情况下想要 return 一个 NonRectError
。
有了所有这些背景,这里是代码:我需要防止减法溢出错误,return 特殊情况下的 NonRectError
:
fn compute_summed_area_table(grid: &Vec<Vec<isize>>) -> Result<Vec<Vec<isize>>, NonRectError> {
let mut summed_area_table =
vec![Vec::with_capacity(grid[0].len()); grid.len()];
for (yi, row) in grid.iter().enumerate() {
for (xi, &value) in row.iter().enumerate() {
let (prev_row, prev_column_idx) = (
yi.checked_sub(1).and_then(|i| summed_area_table.get(i)),
xi.checked_sub(1)
);
let summed_values =
value +
// I(x, y - 1)
match prev_row {
None => &0,
Some(prev_row_vec) => match prev_row_vec.get(xi) {
Some(v) => v,
None => return Err(NonRectError { xi, yi })
}
} +
// I(x - 1, y)
(prev_column_idx
.and_then(|i| summed_area_table[yi].get(i))
.unwrap_or(&0)) -
// I(x - 1, y - 1)
(prev_row
.map_or(&0, |r| {
prev_column_idx
.and_then(|i| r.get(i))
.unwrap_or(&0)
}));
summed_area_table[yi].push(summed_values);
}
}
Ok(summed_area_table)
}
// Omitted the definition of NonRectError here, but it is defined.
这段代码显然是 sin 本身的定义,但我不确定从哪个角度来简化它 - 天哪,边缘情况太多了!
是否有任何 built-in 方法可以让我转义这种嵌套的 error-checking 内容?我可以用比这更简单的方式 return NonRectError
吗?
您可以尝试以下操作:
使用数组,而不是嵌套
Vec
s。使用数组,可以保证所有行的宽度都一样,不会出现NonRectError
。 (但也许你有充分的理由使用嵌套Vec
s,所以我的其余示例使用嵌套Vec
s。)您计算
summed_value
的块很长。我会这样分解它:// I(x, y - 1) let north = ...; // I(x - 1, y) let west = ...; // I(x - 1, y - 1) let northwest = ...; let summed_values = value + north + west - northwest;
而不是检查减法,检查
xi
和yi
是否为非零更容易。此外,.ok_or()
是将None
转换为错误的好方法。let northwest = match (xi, yi) { (0, _) => 0, (_, 0) => 0, (_, _) => { // We know xi and yi are nonzero, so we can subtract without checks summed_area_table.get(yi - 1) .and_then(|row| row.get(xi - 1)) .ok_or(NonRectError { xi, yi })? } };
你也可以用
if/else
链来写。它们都是惯用的,这只是一个偏好问题。我比较喜欢match
,因为感觉更简洁let northwest = if xi == 0 { 0 } else if yi == 0 { 0 } else { // same as above };