fixnum与nil的比较失败,值不是零

时间:2015-08-20 04:44:17

标签: ruby debugging

我正在编写一个程序,它接受一个数组及其中一个值的索引,然后找到最接近的数字(如果一个领带左侧有优先权)。但是,我似乎无法调试我的程序的一部分作为我的逻辑,甚至输出似乎有价值但是,当它运行时它给出一个错误,Fixnum无法与nil比较,我不明白为什么。任何帮助将不胜感激。

var array=this.array.slice(0);

- 错误讯息 -

Fixnum与nil的比较失败

(repl):11:在`<'

(repl):11:在`nearest_larger'

(repl):30:在`initialize'

3 个答案:

答案 0 :(得分:3)

  

当它运行时会出现一个错误,Fixnum无法与nil进行比较,我不知道为什么

这是因为这个表达式:

until (arr[idx] < arr[i]) || (i == arr.length)

Ruby从左到右评估它,即:

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

until (arr[idx] < arr[i]) || (i == arr.length)
#      arr[ 2 ] < arr[4]
#             4 < nil
#          ArgumentError  

你可以通过交换两张支票来修复它:

until (i == arr.length) || (arr[idx] < arr[i])
#      4 == 4
#       true
由于short-circuit evaluation,Ruby并没有评估右侧。

你并没有要求采用不同的方法,但我无法抗拒找到更紧凑的解决方案。这是我的尝试:

我们必须将起始索引n的值与其相邻值进行比较,即

  • array[n]array[n - 1]
  • 进行比较
  • array[n]array[n + 1]
  • 进行比较
  • array[n]array[n - 2]
  • 进行比较
  • array[n]array[n + 2]
  • 进行比较
  • ...

我首先编写一个以交替顺序返回相邻索引数组的方法,从n开始,即[n - 1, n + 1, n - 2, n + 2, ...]

def adjacent_indices(size, n)
  (1...size).flat_map { |i| [n - i, n + i] }.reject { |i| i < 0 || i >= size }
end

flat_map返回上述数组。 reject是一个边界检查,它会删除低于0或高于数组size的所有索引。

示例:

adjacent_indices(5, 0) #=> [1, 2, 3, 4]
adjacent_indices(5, 1) #=> [0, 2, 3, 4]
adjacent_indices(5, 2) #=> [1, 3, 0, 4]
adjacent_indices(5, 3) #=> [2, 4, 1, 0]
adjacent_indices(5, 4) #=> [3, 2, 1, 0]

看起来不错。

使用Enumerable#find,我们可以轻松找到第一个索引i,其数组值(即array[i])大于索引n的值(即{{1} }}):

array[n]

示例:

def nearest_larger(array, n)
  adjacent_indices(array.size, n).find { |i| array[i] > array[n] }
end

答案 1 :(得分:2)

正如@Amit已经回答了你的问题,我想向你展示一种方式(在许多方面),你可以使你的方法更像Ruby,这在一定程度上意味着更少依赖索引。我将用一个例子来解释这个。

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

如您所见,我们希望最接近的较大值:

target = arr[3]
  #=> 7

答案是8。让我们找到最接近的较高值的索引(当然这会给你这个值)。

我将给出的答案不是最好的方法,但我之所以选择它,是因为通过详细介绍它,你会对Ruby有所了解。

通常,当问题涉及索引时,您要做的第一件事就是写:

arr.each_with_index
  # => #<Enumerator: [2, 8, 4, 7, 3, 8, 5, 9]:each_with_index>

在本讨论中,我们将此枚举器分配给变量:

enum = arr.each_with_index

如您所见,enum是一个枚举器,这意味着它是类Enumerator的一个实例。

我们可以将此枚举器转换为数组,如下所示:

pairs = enum.to_a
  #=> [[2, 0], [8, 1], [4, 2], [7, 3], [3, 4], [8, 5], [5, 6], [9, 7]]

但等等,班级Enumerator没有方法:to_a。这意味着它是从Enumerator的祖先之一继承的,它们是:

Enumerator.ancestors
  #=> [Enumerator, Enumerable, Object, Kernel, BasicObject] 

我们可以查看:to_a的这些(按顺序),但更直接的方法是:

enum.method(:to_a).owner
  #=> Enumerable

果然,它是Enumerator#to_a

这有点转移,但现在让我们使用上面的数组pairs。顺便说一句,你通常会写:

pairs = arr.each_with_index.to_a

而不是像enum这样的中间变量。

pairs的美妙之处在于,通过操纵其元素(双元素数组),arr的每个元素的索引与元素本身一起被携带。

试试这个:

before = (idx.zero? ? [] : pairs[0..idx-1]).reverse
  #=> [[4, 2], [8, 1], [2, 0]] 
after  = pairs[idx+1..-1]
  #=> [[3, 4], [8, 5], [5, 6], [9, 7]] 

现在解决方案已近在咫尺。我们只想逐步浏览beforeafter的元素,直到对中的第一个元素大于target #=> 7

此时你可能想知道我是否真的简化了这个问题。耐心点。我这样做是为了使用一些Ruby方法。首先,让我们使用Enumerable#zip以方便的方式合并beforeafter。 (您正在阅读链接中的方法定义,不是吗?)

在这样做之前,使beforeafter的长度相同是很方便的。一种方法如下:

mx = [before.size, after.size].max
  #=> 4
if before.size < mx
  before.concat [[target, nil]]*(mx-before.size)
elsif after.size < mx
  after.concat [[target, nil]]*(mx-before.size)
end
before
  #=> [[4, 2], [8, 1], [2, 0], [7, nil]] 
after
  #=> [[3, 4], [8, 5], [5, 6], [9, 7]]

如您所见,[7, nil]附加了一个元素before。我使用target的值作为第一个元素,因此永远不会被选中。

我们现在准备zip

pairs_of_pairs = before.zip(after)
  #=> [[[4, 2], [3, 4]], [[8, 1], [8, 5]],
  #    [[2, 0], [5, 6]], [[7, nil], [9, 7]]] 

这个数组有四个元素,第一个是[[4, 2], [3, 4]]

我们现在可以使用Enumerable#find来获取最接近的较高值的索引:

p = pairs_of_pairs.find { |(b_val,_), (a_val,_)| [b_val, a_val].max > target }
  #=> [[8, 1], [8, 5]]

(暂时忘记我编写块变量的方式。)

因此p包含pair_of_pairs[b_val, a_val].max > target评估true的第一个元素。如果arr中的值不大于target,则p将等于nil

由于p不是nil,所以剩下的就是确定p的哪个元素包含高于target的值并返回相应的索引。由于关系位于arr左侧,我们首先检查before

i = (p.first.first > target) ? p.first.last : p.last.last
  #=> 1

,值为:

arr[i]
  #=> 8

总之,我们可以写:

def nearest_larger(arr, idx)
  target = arr[idx]
  pairs = arr.each_with_index.to_a
  before = (idx.zero? ? [] : pairs[0..idx-1]).reverse
  after  = pairs[idx+1..-1]
  mx = [before.size, after.size].max
  if before.size < mx
    before.concat [[target, nil]]*(mx-before.size)
  elsif after.size < mx
    after.concat [[target, nil]]*(mx-after.size)
  end
  p = before.zip(after).find { |(b_val,_), (a_val,_)|
    [b_val, a_val].max > target }
  p ? ((p.first.first > target) ? p.first.last : p.last.last) : nil
end

nearest_larger(arr, 3)
  #=> 1
nearest_larger(arr, 7)
  #=> nil

要清理的最后一件事涉及看起来奇怪的块变量:

p = pairs_of_pairs.find { |(b_val,_), (a_val,_)| [b_val, a_val].max > target }

find调用Array#eachpairs_of_pairs的每个元素传递到块中。第一个元素是[[4, 2], [3, 4]]。为块分配值使用“并行分配”和“消除歧义”:

(b_val,_), (a_val,_) = [[4, 2], [3, 4]]
  #=> [[4, 2], [3, 4]] 
b_val
  #=> 4 
a_val
  #=> 3 
_ #=> 3 

我使用下划线(局部变量的有效名称!)来引起注意我在块计算中没有使用它。

答案 2 :(得分:1)

使用to_i,将nil转换为0 -

   def nearest_larger(arr, idx)
     i = idx
     until  arr[i].to_i > arr[idx].to_i || i.to_i == 0
      i -=1
     end
     left = arr[i]
     lefti = i
     i = idx
     puts arr[idx] #4
     puts arr[i] #4 (functions as nil in next line)
     until (arr[idx].to_i < arr[i].to_i) || (i.to_i == arr.length)
      i += 1
     end
     right = arr[i]
     righti = i
     puts righti
     if  righti.to_i == arr.length && lefti.to_i == 0
       return nil
     elsif right == left
       return lefti
     elsif right.to_i < left.to_i
      return lefti
     elsif right.to_i > left.to_i
      return righti
     else
      print "idk man"
     end
   end

   nearest_larger([2,8,4,3], 2)