Вот два способа, которые можно сделать без использования рекурсии.
Используйте each
для генерации элементов нужного массива, пока не будет достигнута целевая пара
def permute(arr1, arr2, last_pair = [])
arr1.each_with_object([]) do |e1,a|
arr2.each do |e2|
a << [e1, e2]
break a if [e1, e2] == last_pair
end
end
end
permute(["a","b"],[1,2],["b", 1])
#=> [["a", 1], ["a", 2], ["b", 1]]
permute(["a","b"],[1,2],["b", 99])
#=> [["a", 1], ["a", 2], ["b", 1], ["b", 2]]
permute(["a","b"],[1,2])
#=> [["a", 1], ["a", 2], ["b", 1], ["b", 2]]
permute(["a","b"],[],["b", 1])
#=> []
permute([],[1,2],["b", 1])
#=> []
permute([],[],["b", 1])
#=> []
Отображение последовательности индексов нужного массива
def permute(arr1, arr2, last_pair = [])
n1 = arr1.size
n2 = arr2.size
idx1 = arr1.index(last_pair.first)
idx2 = idx1.nil? ? nil : arr2.index(last_pair.last)
return arr1.product(arr2) if idx2.nil?
0.step(to: idx1*n2+idx2).
map {|i| [arr1[(i % (n1*n2))/n2], arr2[i % n2]]}
end
permute(["a","b"],[1,2],["b", 1])
См. Числовой # шаг
idx1*n2 + idx2
, количество элементов в возвращаемом массиве вычисляется следующим образом.
last_pair = ["b", 1]
n2 = arr2.size
#=> 2
idx1 = arr1.index(last_pair.first)
#=> 1
idx2 = idx1.nil? ? nil : arr2.index(last_pair.last)
#=> 0
idx1*n2 + idx2
#=> 2
Элемент с индексом i
возвращаемого массива:
n1 = arr1.size
#=> 2
[arr1[(i % (n1*n2))/n2], arr2[i % n2]]
#=> [["a","b"][(i % 2*2)/2], [1,2][i % 2]]
Для i = 1
это
[["a","b"][(1 % 4)/2], [1,2][1 % 2]]
#=> [["a","b"][0], [1,2][1]]
#=> [“a”, 2]
Для i = 2
это
[["a","b"][(2 % 4)/2], [1,2][2 % 2]]
#=> [["a","b"][1], [1,2][0]]
#=> [“b”,1]
Обратите внимание, что мы не можем написать
arr1.lazy.product(arr2).first(idx1*n2+idx2+1)
, потому что arr1.lazy
возвращает перечислитель (arr1.lazy
#=> #<Enumerator::Lazy: ["a", "b"]>
), но Array # product требует, чтобы его получатель был массив. По этой причине некоторые Rubyists хотели бы, чтобы product
сделал метод Enumerable
(с версией lazy
), но не задерживайте дыхание.