我正在编写一个程序,它接受一个数组及其中一个值的索引,然后找到最接近的数字(如果一个领带左侧有优先权)。但是,我似乎无法调试我的程序的一部分作为我的逻辑,甚至输出似乎有价值但是,当它运行时它给出一个错误,Fixnum无法与nil比较,我不明白为什么。任何帮助将不胜感激。
var array=this.array.slice(0);
- 错误讯息 -
Fixnum与nil的比较失败
(repl):11:在`<'
(repl):11:在`nearest_larger'
(repl):30:在`initialize'
答案 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]]
现在解决方案已近在咫尺。我们只想逐步浏览before
和after
的元素,直到对中的第一个元素大于target #=> 7
。
此时你可能想知道我是否真的简化了这个问题。耐心点。我这样做是为了使用一些Ruby方法。首先,让我们使用Enumerable#zip以方便的方式合并before
和after
。 (您正在阅读链接中的方法定义,不是吗?)
在这样做之前,使before
和after
的长度相同是很方便的。一种方法如下:
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#each将pairs_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)