Rust:binary_search 系易错的案例代码

本文探讨了在Rust中使用二分查找时需要注意的排序问题,强调了数据必须保持升序的重要性。通过示例展示了如何正确使用`binary_search_by_key`方法,并解释了错误的布尔判断逻辑可能导致的降序结果。同时,文章提到了使用迭代器处理Option类型的便捷性。
摘要由CSDN通过智能技术生成

在 playground 中运行

有时候需要慎重选择 true 还是 false 。

#[derive(Debug)]
struct User { id: u32, pid: u32, name: String, }

fn set_up_option() -> Vec<Option<User>> {
    // 注意修改了顺序
    vec![Some(User { id: 0, pid: 1, name: "admin1".to_string(), }),
         Some(User { id: 2, pid: 2, name: "admin3".to_string(), }),
         Some(User { id: 1, pid: 1, name: "admin2".to_string(), }),
         Some(User { id: 3, pid: 2, name: "admin4".to_string(), })]
}

/// binary_search 系的方法有非常重要的前提:sorted
/// 这里说的排序专指升序方法,它并不支持降序数据,例子:
/// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4696a39e4e60c9498a2be05a1c7e9c64
///
/// 然而这的数据有几个地方可能不满足 binary_search 的前提:
/// 1. 结构体可能需要支持排序 :
///    #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone)]
/// 2. 上一条不是必须的,但这一条必须满足,你得让你的数据是升序的,因为 binary_search
///    不会给你排好序。要么你十分确信数据按照某种方式升序(比如这里按照 pid 升序),
///    要么使用 `sort` 或者 `sort_by_key` 让数据升序,这里更建议使用后者,因为前者需要修改一些代码
///    (比如添加第一条所列的 traits、并且让结构体的 pid 字段放在首位)
/// 3. 此外,binary_search_by_key 比较的序列依然必须是升序的,而使用 bool 时,
///    true / false 的判断逻辑会影响原升序数据的逻辑结果是降序或者升序,见下面的例子
/// 4. 所以,必须始终确保 binary_search 的数据升序,否则针对小型数据,选择迭代器省事
fn main() {
    // binary_search_by_key: 检索 pid 为 1 的 user

    let mut users_option = set_up_option();

    users_option.sort_by_key(|x| x.as_ref().unwrap().pid); // 还需处理这里可能出现的 None ,而使用迭代器,None 很容易处理
    // println!("sorted: {:#?}", users_option);

    // wrong
    let res = users_option.binary_search_by_key(&true, |x| matches!(x, Some(user) if user.pid == 1));
    println!("\npid == 1 [wrong]: {:?}", res); // Err(4)

    // correct
    let res = users_option.binary_search_by_key(&false, |x| matches!(x, Some(user) if user.pid != 1));
    println!("pid == 1 [correct]: {:?}", res); // Ok(1)

    println!("\n这里 true / false 的逻辑等价于 ↓");
    // wrong
    let m: Vec<u8> = users_option.iter().map(|x| matches!(x, Some(user) if user.pid == 1) as u8).collect();
    let res = m.binary_search(&1);
    println!("not ascending: {:?}", m); // [1, 1, 0, 0]
    println!("thus [wrong]: {:?}", res); // Err(4)

    // correct
    let m: Vec<u8> = users_option.iter().map(|x| !matches!(x, Some(user) if user.pid == 1) as u8).collect();
    let res = m.binary_search(&0);
    println!("ascending: {:?}", m); // [0, 0, 1, 1]
    println!("[correct]: {:?}", res); // Ok(1)

    // 使用迭代器就无需排序,而且无需给结构体添加不必要的 traits,处理含 Option 或 Result 的数据也十分方便
    // 尤其是,有时候你根本无法给结构体添加 `Eq` `Ord` (比如结构体字段类型有浮点型)
    let users_option = set_up_option();

    let f = users_option.iter().enumerate() // 你需要索引的话就加这个
                        .filter(|&(_, x)| matches!(x, Some(user) if user.pid == 1));

    let res = f.clone().next().unwrap(); // 你只需要第一个出现的就使用 `next`
    println!("\nuse iter [correct]: {:?}", res); // (0, Some(User { id: 0, pid: 1, name: "admin1" }))

    let res: Vec<_> = f.collect(); // 需要所有出现的结果就 `collect`
    println!("use iter [correct]: {:#?}", res); // [(0, ...), (2, ...)]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值