Arrays Ruby:查找数组中下一个匹配项的索引,或使用偏移量查找
我想在Arrays Ruby:查找数组中下一个匹配项的索引,或使用偏移量查找,arrays,ruby,Arrays,Ruby,我想在Array#find_index{| item | block}首次匹配之后找到更多匹配项。如何搜索第二个匹配项、第三个匹配项等的索引 换句话说,对于数组#find_index,我需要与pos参数等价的Regexp#match(str,pos)。然后我可以维护当前位置索引以继续搜索 我不能使用Enumerable#find_all,因为我可能会在调用之间修改数组(在这种情况下,我还将调整当前位置索引以反映修改)。我不想复制数组的一部分,因为那样会增加算法的效率。我希望在不复制阵列的情况下执
Array#find_index{| item | block}
首次匹配之后找到更多匹配项。如何搜索第二个匹配项、第三个匹配项等的索引
换句话说,对于数组#find_index
,我需要与pos
参数等价的Regexp#match(str,pos)
。然后我可以维护当前位置索引以继续搜索
我不能使用Enumerable#find_all
,因为我可能会在调用之间修改数组(在这种情况下,我还将调整当前位置索引以反映修改)。我不想复制数组的一部分,因为那样会增加算法的效率。我希望在不复制阵列的情况下执行此操作:
new_pos = pos + array[pos..-1].find_index do |elem|
elem.matches_condition?
end
以下是不同的问题。他们只询问数组中的第一个匹配项,外加一个:
- 当然,一种方法是:
new_pos = pos + (pos...array.size).find_index do |index|
elem = array[index]
elem.matches_condition?
end
然而,这是笨拙的,很容易出错。例如,您可能忘记添加pos
。此外,您必须确保elem
没有隐藏某些内容。这两种情况都会导致难以追踪的bug
我发现很难相信
Array#find#u index
和Array#index
的索引参数仍然没有进入语言。然而,我注意到,直到1.9版才出现了Regexp#match(str,pos)
,这同样令人惊讶。一种更简单的方法就是:
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
这可能看起来很可怕。然而,假设
prepare\u for\u condition
一直在以小的方式进行调整。这些调整最终将得到重构;然而,到那时,重构代码的输出也将以一些不属于旧的重构代码的小方式得到调整,但似乎还不能证明它们自己的重构是合理的——等等。有时,有人会忘记改变这两个地方。这似乎是病态的;然而,正如我们所知,在编程中,病理病例有一个习惯,就是发生得太频繁。这里有一种方法可以做到这一点。我们可以在Array
类中定义一个新方法,该方法允许我们查找与给定条件匹配的索引。条件可以指定为返回布尔值的块
新方法返回一个枚举器
,这样我们就可以从许多枚举器
方法中获益,例如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
类中添加了一个新方法。解决方案可以很容易地适应工作,而无需使用猴子补丁
arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }
对于findees
中的每个元素n
,我们需要arr
的第一个不匹配元素m
的索引,用于哪个过程[n]==m
。例如,如果n=3
,则proc[3]#==>9
,因此arr
中的第一个匹配索引将是0
。对于findees
中的下一个n=3
,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#find_索引。这是简洁的表格
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类中为什么要发布一个“笨拙且容易出错”的答案?因为在某些情况下,这将是您唯一(或最好)的选择,除非使用monkey patching。我想说服Ruby团队,他们应该在标准库方法“find#u index”(可能还有其他方法)中添加一个position(或offset)参数。我喜欢你的方法,但你做得太多了。
枚举器中的所有内容。新的
块可以替换为每个带索引的{item,idx | yielder.yield(idx)if block.call(item)}
。看一看:谢谢@Jordan-我已经根据你的建议更新了我的答案。但是如果我在调用e.next之间修改数组,这是否仍然保证有效?@martinjs不确定你在说什么样的修改。如果更新已遍历的元素,则在执行枚举器#rewind
class Array
def find_indices(*args)
h = each_with_index.with_object(Hash.new {|h,k| h[k] = []}) { |(n,i),h| h[n] << i }
args.each_with_object([]) { |n,a| v=h[yield n]; a << v.shift if v }
end
end
arr.find_indices(*findees) { |n| n**2 }
#=> [1, 5, 0, nil, 4, nil]
arr = [3,1,2,1,3,6,5]
findees = [1,6,3,6,3,7]
arr.find_indices(*findees, &:itself)
#=> [1, 5, 0, nil, 4, nil]
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