我有以下结构:
struct Thing {
id: usize,
cost: u32,
numbers: Vec<usize>
}
我正在考虑以下三个
Thing
对象和一个包含一些 Id 的向量 chain_of_things
:
let some_things = [
Thing { id: 0, cost: 10, numbers: vec![1, 2] },
Thing { id: 1, cost: 15, numbers: vec![3] },
Thing { id: 2, cost: 20, numbers: vec![0, 1] },
Thing { id: 3, cost: 5, numbers: vec![1] }
];
let chain_of_things = [0, 1, 3];
请注意,为了方便起见,向量
Thing
中的 some_things
的 Id 与其在向量中的位置一致。如果 (thing_1, thing_2)
的 id
包含在 thing_2
中,则一对 thing_1.numbers
是可行的。
任务是找出给定的
chain_of_things
是否可行,这意味着对于每个索引i
,(some_things[chain_of_things[i]], some_things[chain_of_things[i+1]])
对必须是可行的。如果这条链是可行的,我想返回事物的 cost
的总和。否则,我想返回一个错误。
使用
for
循环解决此任务很简单。我想使用对 map
的调用和内部对 find
的调用来解决它。到目前为止我的方法如下:
let mut cost = some_things[chain_of_things[0]].cost;
let results: Vec<_> =
chain_of_things[..chain_of_things.len() - 1]
.iter()
.enumerate()
.map(|(index, thing_id)| {
if let Some(number) = some_things[*thing_id].numbers
.iter()
.find(|&&x| {x == chain_of_things[index + 1] })
{
cost += some_things[chain_of_things[index + 1]].cost;
cost
}
else {
u32::MAX
}
}).collect();
这可行,但有多个缺点:
result
向量来搜索最大值以确定所有对是否可行。map
迭代器就不会停止。如何实现规避上述缺点的行为?
我的问题似乎与:当 Iterator::map 返回 Result::Err 时如何停止迭代并返回错误?
我认为解决方案是有一个额外的
find
函数返回 Result<T, E>
。但我调用的 find
函数返回一个 Option<T>
。我可以编写自己的 find
函数,但它只会包装 Option<T>
并以某种方式将其转换为 Result<T, E>
,这似乎有点过分了。
try_fold()
而不是 map()
。
据我了解,您的目标是如果链中的每一对都可行,则返回成本总和,否则返回错误,因此如果索引不可行,您传递给
try_fold()
的闭包应返回None
,否则Some(cost_running_total)
。您最终将得到一个可选值,如果遇到不可行的索引,该值将提前显示 None
,否则如果所有项目均可行,它将包含总和。
windows(2)
迭代 chain_of_things
中的相邻条目对来简化代码。
代码看起来像这样:
let result = chain_of_things
.windows(2)
.map(|ids| (&some_things[ids[0]], &some_things[ids[1]]))
.try_fold(
some_things[chain_of_things[0]].cost,
|acc, (thing1, thing2)| {
thing1
.numbers
.contains(&thing2.id)
.then_some(acc + thing2.cost)
},
);
match result {
Some(cost) => println!("Total feasible cost: {cost}"),
None => println!("Not feasible"),
}
这个解决方案看起来相当惯用。您可以将打印添加到
map(|pair| ...
行,以验证在遇到第一个坏对后,链不会被进一步处理
let chain_of_things = [0,1,3,0,2,3,2,1,2,3];
let sum_cost = chain_of_things
.map(|i| &some_things[i]) // get things by indices
.windows(2) // iterate pairs in chain
.map(|pair| pair[0].numbers
.contains(&pair[1].id)
.then(|| pair[1].cost)
.ok_or(())
) // get cost if feasible, else Result::Err
.sum::<Result<u32, ()>>() // Result implements Sum, stopping iteration on first Err
.and_then(|sum| Ok(sum + some_things[chain_of_things[0]].cost)); // add first thing's cost
println!("Answer: {:?}", sum_cost);