扩展(猴子修补)二进制搜索数组类和语法糖

时间:2019-04-08 20:44:19

标签: ruby

我一直在研究一些搜索算法,而我的最后一个问题归结为二进制搜索。我观看了一些youtube视频以了解该概念,然后尝试解决该问题,但不断出现无休止的循环错误。我已经检查了堆栈溢出和reddit,以及Google会带我到哪里去,但我找不到适合我的编码方法的解决方案。另外,请原谅“猴子修补”一词,引起我注意的是,该技术术语被称为“扩展”,所以我的指导老师将其作为“猴子修补”来教给我们的是错。

这是我的代码:

class Array
   def my_bsearch(target)

    return nil if self.empty?

    middle_idx = self.length/2

    left = self.take(middle_idx)
    right = self.drop(middle_idx + 1)

    return middle_idx if self[middle_idx] == target

    until self[middle_idx] == target || self.nil? == nil
      if self[middle_idx] < target
        right.my_bsearch(target)
      elsif self[middle_idx] > target
        left.my_bsearch(target)
      end
    end

  end
end

我有一个解决方案,但是我不想只记住它-我在理解它时遇到了麻烦;当我尝试翻译它,学习它并将我所缺少的东西实现到自己的代码中时。

class Array
  def my_bsearch(target)
    return nil if size == 0
    mid = size/2

    case self[mid] <=> target
    when 0
      return mid
    when 1
      return self.take(mid).my_bsearch(target)
    else
      search_res = self.drop(mid+1).my_bsearch(target)
      search_res.nil? ? nil : mid + 1 + search_res
    end
  end
end

我猜我了解情况,尽管不习惯使用它。我尝试使用调试器来跟踪它,但是我认为我对ELSE部分中发生的事情挂了电话。虽然语法糖显然比我的逻辑更简洁,但对我的红宝石知识水平的人来说并不简单明了。所以,是的,我的无知是我猜到的大部分问题。

有没有一个识字能力和耐心的人,能够帮助我将其分解为我可以更好地理解的东西,以便我可以从中学习?

2 个答案:

答案 0 :(得分:1)

首先,takedrop具有足够相似的界面,您实际上并不想放下+ 1。如果这样做,它将忽略数组中的一个元素。

接下来,对于此类的实例,self.nil?将始终为false(并且永远不会为nil)。实际上,.nil?是一种完全避免必须与nil==进行比较的方法。

您想要self.empty?。此外,除设置器外,默认情况下,在Ruby中,消息会发送到self。换句话说,self.是有用的前缀的唯一时间是消息以=结尾并且像self.instance_var = 'a constant'那样作为左值运行,因为没有self.,标记instance_var =将被解释为局部变量,而不是实例变量设置。这里不是这种情况,因此empty?就足够了self.empty?

答案 1 :(得分:0)

所以我想出了办法,所以我决定回答我自己的帖子,以希望在遇到此问题时能帮助其他人。

因此,如果我有一个Array,并且目标是middle_element,则它将报告middle_element_idx。没关系。如果目标小于middle_element怎么办?它递归搜索原始数组的左侧。找到后,将报告left_side_idx。这样做没有问题,因为数组中的元素从左到右依次计数。因此,它从0开始并上升。

但是如果目标位于中间元素的右侧怎么办?

好吧,搜索右侧很容易。逻辑与向左搜索相对。递归完成。如果在右侧找到了target_idx,它将返回一个target_idx-但是,这是在右侧数组中找到的目标的IDx!因此,您需要获取返回的target_idx并将其添加1并添加原始的middle_element_idx。见下文:

  def my_bsearch(target)

    return nil if self.empty?

    middle_idx = self.length/2
    left = self.take(middle_idx)
    right = self.drop(middle_idx + 1)

      if self[middle_idx] == target
        return middle_idx
      elsif self[middle_idx] > target
        return left.my_bsearch(target)
      else
        searched_right_side = 1 + right.my_bsearch(target)
        return nil if searched_right_side.nil? == true
        return searched_right_side + middle_idx
      end

  end
end

请注意此解决方案还有多少行?与case / when和三元方法结合使用的飞船运算符将大大减少行数。

根据蒂姆的建议/反馈,我将其更新为:

  def my_bsearch(target)

    return nil if empty?

    middle_idx = self.length/2
    left = self.take(middle_idx)
    right = self.drop(middle_idx)

      if self[middle_idx] == target
        return middle_idx
      elsif self[middle_idx] > target
        return left.my_bsearch(target)
      else
        searched_right_side = right.my_bsearch(target)
        return nil if searched_right_side.nil?
        return searched_right_side + middle_idx
      end

  end
end