惯用的生锈? 实施二进制搜索(第2部分)

2021-06-13 08:51:23

这是原始文章的后续行动,我看几种Wathsto提高了我的二进制搜索的生锈实施 - 重点删除'错误'并使它成为'惯用'尽可能。

我在第一个与中间值和高/低光标之间的比较相关的第一篇文章中收到的最常见的反馈。

在我的原始推文中(甚至在第一篇文章之前)我介绍了通过生锈' s匹配构造对此比较的算法的变种。

铁锈酒吧fn(k:i32,项目:& [i32]) - >选择< I32> {让MUT LOW:I32 = 0;让MUT HIGH:I32 = items.len()作为i32 - 1;虽然低

如果我们将第一篇文章的反馈应用于这一实施,我们将&#39' D结束了以下 - 这只是一个灰尘,因为它可以消除一些铸件以及手册'地板'我最初在做。

铁锈酒吧fn(k:i32,项目:& [i32]) - >选择< Usize> {让MUT低:USIZE = 0;让mut高:Usize = items.len() - 1;虽然低

这是它变得有点复杂的地方。我最初接受了Twitterthat的一些反馈,建议,因为I' m不使用任何臂中的匹配块的返回值,以及我' m只使用它来突变高/低光标,这这可能被视为不惯然的生锈......

我完全明白反馈,并纠正匹配可以写得如此:

Rust匹配项目。(中间为大小){一些(当前)如果* Current == k => {返回一些(中间); },一些(当前)如果*当前> k => {如果中间== 0 {返回无};高=中间 - 1; },某些(电流)如果*电流< k => {Low =中间+ 1; },_ => {}}

现在,每个匹配臂都没有尾随表达式,可能会更清楚我们在那里' t打算从匹配块返回这里的值。

1:rust致力于使用模式匹配,而匹配在技术上是技术性的(因此可以用作一个值),我不觉得它觉得它'如果它并非总是如此用作价值?

2:如果匹配的每个ARM具有不同的语义,那似乎会更多的问题。例如,如果Onearm返回一个值,另一个Didn' t。这将使实施不一致,并且该观点可以持有更多的水,但是Rust' s型系统无论如何都不会允许这种情况,因为匹配的每个arm必须返回相同的值。

对于那些两个原因,这篇文章的其余部分将专注于进一步改进我们的算法,充分利用匹配 - 但如果你不同意,我会在Twitter上伸出援手,我' D Loveve继续谈话♥

我们的算法在I32值的一片中搜索。在每次迭代中,我们正在访问.get()方法的Slice中的值。这是为了安全完成,因为如果您尝试索引 - 使用缺失的值,可以发生运行时恐慌。

rust fn(){让项目= vec![10,40,90];让rest_item =项目[3]; println!(" {:?}",thath_item)//#39; main' &#39恐慌;索引超出界限:// len是3但索引是3'}

rust fn(){让项目= vec![10,40,90];让First_Item = Items.get(0); println!(" {:?}",first_item); //打印`某些(10)`letth_item = items.get(3); println!(" {:?}",thath_item)//打印`none`}

因此,访问值的第二种方式可以更安全,但它以该选项类型的形式的额外层间间接以成本为代价。

Rust //返回类型是选项< i32>让项目= items.get(4)//返回类型是i32,但可能会恐慌让项目=项目[4]

这在第一篇文章中影响了我们的算法,因为我们需要使用如果LetExpression通过某些(当前)模式暴露当前值。

rust //↓↓↓↓↓↓↓↓↓↓↓↓如果让某些(当前)= items.get(中间){if * current == k {// snip}如果* current> k {// snip}如果*当前< k {// snip}}

因此,要删除选项,因此还删除了.get()和一些(当前),我们需要在数学上进行数学上的界限 - 否则我们' d and to and and#39;事实证明,我们的原始算法实际上正在执行我们所需的所有检查 - 它始终确保下一个索引访问高于零,并且低于切片的最大长度。

这意味着在原始文章方面,我们可以&#39更换了如果让某些(当前)用一个简单的索引传播,这使得解决方案较少嘈杂并且整体更简单。

生锈//我们知道这是赢得'自从我们控制'中间' //↓↓↓↓↓↓↓↓↓↓↓↓↓当Current == k {// Snip}

现在我们可以相信直接索引访问始终是安全的,因为我们控制了中间价值,但Doesthis如何帮助我们提到的匹配表达式?

如果我们所做的一切都从本文的开头取出比赛表达式,并将选项删除为提到的,我们' d最终得到了这样的东西

rust //在匹配项目之前。注(中间){某些(当前)如果* current == k =>返回一些(中间),如果*当前> k => {如果中间== 0 {返回无}; HIGH =中间 - 1},如果*电流< k => {Low =中间+ 1; },_ => {}} //匹配项目[中间] {当前电流== k => {返回一些(中间); },当前if * current> k => {如果中间== 0 {返回无};高=中间 - 1},电流IF *电流< k => {Low =中间+ 1; },_ => {}}

这几乎甚至是一个改善♥!如果有的话,我' d说它&#39实际上更难阅读。

它缺少下一个大步骤,这也是另一个朝向更惯用的生锈 - 而且达到了ord特征在标准图书馆的i32实施的事实。

ord特性有一个.cmp(其他)方法,它返回订购枚举的变体,它' s定义列表...

rust // std :: cmp pub特性:eq + partialord<自我> {FN(&自我,其他:& self) - >订购; // Snip}

rust fn(){让第一:i32 = 1;让第二:i32 = 3;让结果= First.cmp(&第二); println!("结果= {}"结果)//输出`少,`更大的`或`等于}

请注意,如何将3个单独的比较合并为单个方法调用 - it删除手动检查等于的需要,更大,更少,更低,而是自我们&#39以来的更高的声明性;重新确定实际实现。

调用后.CMP(其他)我们现在有一个值等于订购的一个值和#39; S 3 Enum Variantsand,这是与模式匹配的语言非常闪耀,因为我们可以在变体上做匹配,并实现了匹配一点点。

锈匹配项目[中间] .cmp(& k){订购::等于=>返回一些(中间),订购::更高=> {如果中间== 0 {返回无}; high = middle - 1},订购::少=>低=中间+ 1}

随着更宣言的风格,它' S也远远少得多,令人沮丧地解析。 😇

此外,由于订购枚举上的匹配是穷的,因此我们不再需要空_ => {}作为最终捕获 - 所有匹配臂。 🙏

此处的最后一件事是因为'重新调用为ord特征实现的方法,这意味着在将来可以更通用,以便在寻找实现ord的任何类型,而不是i32的算法我们的例子。这将使这是一个伟大的后续博客文章,突出了Rust的特征的力量,如果你喜欢看到这个帖子发生在Twitter上,请在Twitter上发出特征。

到目前为止申请两种改进后(去除选项+做单一比较),我们最终得到了以下实现 - 这显然是一个很大的改善♥。

Rust使用STD :: CMP ::订购; FN(k:I32,项目:& [I32]) - >选择< Usize> {如果项目.is_empty(){return none}让mut低:Usize = 0;让mut高:Usize = items.len() - 1;虽然低

我们的实施中有2条线条仍然感觉'过度设计的' - 或者相反,它感觉可能会改善或移除。

Frose //这是函数中的开头检查如果Items.Is_empty(){返回None} //部分)需要//确保我们在中间==中的DON' T减去低于零点0 {返回none};

要解决此问题,我们需要解决核心算法。如果我们将高度定义为独占的上个边界,只需将其重新分配给函数开始时切片的长度,或者到后域值,我们可以确定我们' ll永远不会减少它的值零,因此我们可以删除上面提到的检查。

我们' D是确保没有一个可变的使用光标可以下降到零以下,这将允许我们删除Thosetwo手动检查。

这让我们留下了以下内容,其中我开始感到快乐,这两个长的博客帖子&我在Twitter上收到的反馈🙏

Rust使用STD :: CMP ::订购; FN(k:I32,项目:& [I32]) - >选择< Usize> {让MUT低:USIZE = 0;让mut高:Usize = items.len();虽然低<高{让中间=(高+低)/ 2;匹配项目[中间] .cmp(& k){订购::等于=>返回一些(中间),订购::更高=>高=中间,订购::少=> Low = Middle + 1}}无}

此处的第一次可视化展示了两个帖子中的所有演示(除了来自The File Change)的所有演示

下一个可视化显示,当您高分之类时,一切都变得更加简单,整体上占用了较少的步骤。另请注意HighCursor如何以比列表结尾的1索引开始,而先前则直接植入最后一个元素。

感谢Wiebe Cnossen和Pasile Henry for提供激发此后续帖子的反馈。

我喜欢听到任何反馈或替代方法来实现这种算法 - 所以如果你有任何想法,请在Twitter上伸出