有人可以解释为什么这行不通吗?有时候我没有

时间:2019-03-21 03:00:57

标签: ruby

所以我正在解决这些练习问题,也许有更好的方法可以做到这一点,但这就是我所得到的。我感到非常困惑,为什么有时候这似乎只起作用...

def nearest_larger(arr, idx)
bigs = []

arr.each do |num|
    if num > arr[idx]
      bigs << num
    end
end

sorted = bigs.sort
    if sorted.length > 1
      sorted = sorted[0]
    end

return arr.index(sorted)
end

puts nearest_larger([2, 3, 4, 8], 2) #DOES NOT WORK
puts nearest_larger([2, 3, 4, 8], 2) #DOES WORK
puts nearest_larger([8, 8, 4, 2], 2) #ALSO WORKS... why?!!!

2 个答案:

答案 0 :(得分:1)

由于我不知道您正在执行的练习,因此我不清楚该函数的确切功能,但是我看到的最明显的问题是您有时在检查一个数组是否包含另一个数组,有时检查它的实际元素。可疑块尤其是if sorted.length > 1; sorted = sorted[0]; end

在您的第一个无效呼叫中,只有一个数字大于索引2,即8。sorted在这种情况下的长度仅为{{1} }-因此,由于您的if语句,它不会将其从数组更改为第一项。

从某种意义上讲,第二种情况也不起作用。

您的代码的经过稍微修改的工作版本如下。

1

较小的版本会这样:

def nearest_larger(arr, idx)
  larger_numbers = []

  arr.each do |num|
    if num > arr[idx]
      larger_numbers << num
    end
  end

  smallest_larger_number = larger_numbers.sort()[0]

  return arr.index(smallest_larger_number)
end

答案 1 :(得分:1)

给出一个数组a和一个数组索引i,假设idxaj的索引a的数组,其中a[j] > a[i] 。如果idxa为空,则返回nil;否则问题是在k中找到一个索引idxa的索引,其中(k-i).abs是最小值。如果idx包含多个索引k,而索引(k-i).abs最小,则可以返回这些索引中的任何一个。

选择索引并找到最接近的

这是解决问题的简单但不是特别有效的方法。

def nearest_larger(arr, idx)
  v = arr[idx]
  a = arr.each_index.select { |i| arr[i] > v }
  return nil if a.empty?
  a.min_by { |i| (i-idx).abs }
end

test = [
  [[2, 3, 4, 8], 2],
  [[8, 5, 4, 3], 2],
  [[8, 3, 4, 2], 2],
  [[8, 5, 8, 2], 2]
]

test.each do |arr, idx|
  i = nearest_larger(arr, idx)
  puts "#{arr} idx = #{idx}: closest idx = #{i.nil? ? "nil" : i}"
end
[2, 3, 4, 8] idx = 2: closest idx = 3
[8, 5, 4, 3] idx = 2: closest idx = 1
[8, 3, 4, 2] idx = 2: closest idx = 0
[8, 5, 8, 2] idx = 2: closest idx = nil

让我们检查一下arridx的这些值的步骤:

arr = [8, 5, 4, 3]
idx = 2

v = arr[idx]
  #=> 4 
e = arr.each_index
  #=> #<Enumerator: [8, 5, 4, 3]:each_index> 

如您所见,方法Array#each_indexarr上调用时将返回一个枚举数。可以将其视为产生价值的机器。我们可以将枚举器转换为数组,以查看它们将生成什么值:

e.to_a
  #=> [0, 1, 2, 3]

继续

a = e.select { |i| arr[i] > v }
  #=> [0, 1]

e将元素传递到Enumerable#select,然后依次传递到块,将块变量i分配给它们的值。实际上,我们可以写

ee = e.select
  #=> #<Enumerator: #<Enumerator: [8, 5, 4, 3]:each_index>:select> 
ee.to_a
  #=> [0, 1, 2, 3] 
a = ee.each { |i| arr[i] > v }
  #=> [0, 1] 

ee可以被视为化合物枚举器。如果最后一点令人困惑,请暂时忘记它。

继续

return nil if a.empty?

a不为空,因此我们不返回。最后一步如下。

a.min_by { |i| (i-idx).abs }
  #=> 1 

请参见Enumerable#min_by

请注意,我可以将arr.each_index替换为arr.size.times(请参阅Integer#times)或替换为0.up_to(arr.size-1)(请参阅Integer#upto)。

选中任一侧,然后向前移动一个,直到找到更大的值

我们可以以增加复杂性为代价来提高效率。实际上,以下代码不是很像Ruby,并且imo不太吸引人。

def nearest_larger(arr, idx)
  v = arr[idx]
  last = arr.size-1
  iup, idn = idx + 1, idx - 1
  while iup <= last || idn >= 0
    if iup <= last
      return iup if arr[iup] > v
      iup += 1
    end
    if idn >= 0
      return idn if arr[idn] > v
      idn -= 1
    end
  end
  nil
end

test.each do |arr, idx|
  i = nearest_larger(arr, idx)
  puts "#{arr} idx = #{idx}: closest idx = #{i.nil? ? "nil" : i}"
end
[2, 3, 4, 8] idx = 2: closest idx = 3
[8, 5, 4, 3] idx = 2: closest idx = 1
[8, 3, 4, 2] idx = 2: closest idx = 0
[8, 5, 8, 2] idx = 2: closest idx = nil