如果嵌套查找返回 None,如何停止对 map 的调用?

问题描述 投票:0回答:2

我有以下结构:

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();

这可行,但有多个缺点:

  1. 我需要迭代
    result
    向量来搜索最大值以确定所有对是否可行。
  2. 一旦找到第一个不可行的对,
    map
    迭代器就不会停止。

如何实现规避上述缺点的行为?

我的问题似乎与:当 Iterator::map 返回 Result::Err 时如何停止迭代并返回错误?

我认为解决方案是有一个额外的

find
函数返回
Result<T, E>
。但我调用的
find
函数返回一个
Option<T>
。我可以编写自己的
find
函数,但它只会包装
Option<T>
并以某种方式将其转换为
Result<T, E>
,这似乎有点过分了。

rust idioms
2个回答
0
投票

如果我正确理解您想要做什么,那么我认为您想要

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"),
}

0
投票

这个解决方案看起来相当惯用。您可以将打印添加到

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);
© www.soinside.com 2019 - 2024. All rights reserved.