我希望首次在Array#find_index { |item| block }
匹配后找到更多匹配项。如何搜索第二场比赛,第三场比赛等的索引?
换句话说,对于pos
,我希望将Regexp#match(str, pos)
参数等效为Array#find_index
。然后我可以保持当前位置索引以继续搜索。
我无法使用Enumerable#find_all
,因为我可能会在调用之间修改数组(在这种情况下,我还会调整当前位置索引以反映修改)。我不想复制数组的一部分,因为这会增加我算法的computational complexity。我想这样做而不复制数组:
new_pos = pos + array[pos..-1].find_index do |elem|
elem.matches_condition?
end
以下是不同的问题。他们只询问数组中的第一个匹配,加上一个:
以下问题更接近,但仍然无法帮助我,因为我需要在继续下一个之前处理第一个匹配(这种方式也与修改冲突):
答案 0 :(得分:2)
更简单的方法就是:
new_pos = pos
while new_pos < array.size and not array[new_pos].matches_condition?
new_pos += 1
end
new_pos = nil if new_pos == array.size
事实上,我认为这可能比我的其他答案更好,因为它更难以出错,并且没有机会从周围的代码中引入未来的阴影问题。但是,它仍然很笨拙。
如果条件更复杂,那么你最终需要做这样的事情:
new_pos = pos
# this check is only necessary if pos may be == array.size
if new_pos < array.size
prepare_for_condition
end
while new_pos < array.size and not array[new_pos].matches_condition?
new_pos += 1
if new_pos < array.size
prepare_for_condition
end
end
new_pos = nil if new_pos == array.size
或者,上帝保佑,begin ... end while
循环(虽然你遇到了new_pos
的初始值的麻烦):
new_pos = pos - 1
begin
new_pos += 1
if new_pos < array.size
prepare_for_condition
end
end while new_pos < array.size and not array[new_pos].matches_condition?
new_pos = nil if new_pos == array.size
这可能看起来很糟糕。但是,假设prepare_for_condition
是以不同方式进行调整的东西。这些调整最终会被重构;然而,到那个时候,重构代码的输出也将最终以不同于旧的重构代码的小方式进行调整,但似乎还没有证明自己的重构是合理的 - 依此类推。偶尔,有人会忘记改变这两个地方。这似乎是病态的;然而,在编程中,众所周知,病态病例的习惯是经常发生。
答案 1 :(得分:2)
这是一种可以做到的方法。我们可以在Array
类中定义一个新方法,它允许我们找到与给定条件匹配的索引。条件可以指定为返回布尔值的块。
新方法返回Enumerator
,以便我们获得许多Enumerator
方法的好处,例如next
,to_a
等。
ary = [1,2,3,4,5,6]
class Array
def find_index_r(&block)
Enumerator.new do |yielder|
self.each_with_index{|i, j| yielder.yield j if block.call(i)}
end
end
end
e = ary.find_index_r { |r| r % 2 == 0 }
p e.to_a #=> [1, 3, 5]
p e.next
#=> 1
p e.next
#=> 3
ary[2]=10
p ary
#=> [1, 2, 10, 4, 5, 6]
p e.next
#=> 5
e.rewind
p e.next
#=> 1
p e.next
#=> 2
注意:我在Array
课程中添加了一个新方法用于演示目的。解决方案可以轻松调整,无需猴子修补
答案 2 :(得分:1)
当然,一种方法是:
ActionBar
但是,这很笨拙,容易出错。例如,您可能忘记添加new_pos = pos + (pos...array.size).find_index do |index|
elem = array[index]
elem.matches_condition?
end
。此外,您必须确保pos
不会影响某些内容。这两者都可能导致难以追踪的错误。
我发现很难相信elem
和Array#find_index
的索引参数仍然没有成为语言。但是,我注意到版本1.9之前不存在Array#index
,这同样令人惊讶。
答案 3 :(得分:0)
假设
arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }
并且对于n
中的每个元素findees
,我们需要m
的{{1}}的第一个不匹配元素arr
的索引。例如,如果which proc[n] == m
,则为n=3
,因此proc[3] #==> 9
中的第一个匹配索引为arr
。对于0
中的下一个n=3
,findees
中的第一个不匹配匹配位于索引arr
。
我们可以这样做:
4
我们可以将其概括为一个方便的arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }
h = arr.each_with_index.with_object(Hash.new { |h,k| h[k] = [] }) { |(n,i),h| h[n] << i }
#=> {9=>[0, 4], 1=>[1, 3], 4=>[2], 36=>[5], 25=>[6]}
findees.each_with_object([]) { |n,a| v=h[proc[n]]; a << v.shift if v }
#=> [1, 5, 0, nil, 4, nil]
方法,如下所示:
Array
答案 4 :(得分:0)
我的方法与其他方法没有太大区别,但可能打包后的清洁器在语法上与 Array#find_index 相似。这是紧凑形式。
def find_next_index(a,prior=nil)
(((prior||-1)+1)...a.length).find{|i| yield a[i]}
end
这是一个简单的测试用例。
test_arr = %w(aa ab ac ad)
puts find_next_index(test_arr){|v| v.include?('a')}
puts find_next_index(test_arr,1){|v| v.include?('a')}
puts find_next_index(test_arr,3){|v| v.include?('a')}
# evaluates to:
# 0
# 2
# nil
当然,只需稍加重写,您就可以将其修补到 Array 类中