更改数组元素的周围元素

时间:2016-04-17 05:50:33

标签: arrays ruby each

我是初学者,正在做一些练习。我正在尝试操纵一个二维数组,这样如果一个元素是1,那么周围的非对角元素应该改为1:

[[0,0,0,0],
[0,0,1,0],
[0,0,0,0],
[0,0,0,0]]

应该返回

[[0,0,1,0],
[0,1,1,1],
[0,0,1,0],
[0,0,0,0]]

我使用嵌套的each_with_index遇到问题:在我调整左右周围的初始更改后,随着方法的迭代,它会通过之前的调整进行调整并进行不必要的更改。此外,应该更改“底部”元素的行会引发错误:

a = [[0,0,0,0],
     [0,0,1,0],
     [0,0,0,0],
     [0,0,0,0]
    ]

a.each_with_index do |m, n| # n == index of main array
    m.each_with_index do |x, y| # y == index of subarray
        if x == 1
            a[n][y+1] = 1 unless (a[n][y+1]).nil? #right
            a[n][y-1] = 1 unless (a[n][y-1]).nil? #left
            a[n-1][y] = 1 unless (a[n-1][y]).nil? #top
            a[n+1][y] = 1 unless (a[n+1][y]).nil? #bottom--currently giving an error
        end
    end
end

关于如何解决这两个方面的任何建议都会受到欢迎。

2 个答案:

答案 0 :(得分:3)

为了避免上一步的干扰,您可以(深)复制数组并将引用数组与修改数组分开,或者在修改数组之前提取所有相关索引。后者更好。此外,使用平面数组比处理嵌套数组容易得多,因此我将a转换为平展数组b,并在b内工作。

b = a.flatten

b
.each_index.select{|i| b[i] == 1}
.each do
  |i|
  b[i - 1] = 1 if b[i - 1] and i - 1 >= 0
  b[i + 1] = 1 if b[i + 1]
  b[i - 4] = 1 if b[i - 4] and i - 4 >= 0
  b[i + 4] = 1 if b[i + 4]
end

a = b.each_slice(4).to_a
# => [[0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 0, 0]]

答案 1 :(得分:1)

我建议您使用Matrix类。

require 'matrix'

m = Matrix[*a]
  #=> Matrix[[0, 0, 0, 0],
  #          [0, 0, 1, 0],
  #          [0, 0, 0, 0],
  #          [0, 0, 0, 0]] 
row, col = m.index(1)
  #=> [1, 2]
Matrix.build(m.row_size, m.column_size) { |r,c|
  (c-col).abs + (r-row).abs <= 1 ? 1 : 0 }.to_a
  #=> [[0, 0, 1, 0],
  #    [0, 1, 1, 1],
  #    [0, 0, 1, 0],
  #    [0, 0, 0, 0]]

此非矩阵版本(使用方法Array#indexFixnum#divmodArray::newEnumerable#each_slice以及其他一些方法)如下所示。

nrows, ncols = a.size, a.first.size
  #=> [4, 4] 
row, col = a.flatten.index(1).divmod(ncols)
  #=> [1, 2] 
Array.new(nrows*ncols) do |i|
  r, c = i.divmod(ncols)
  (c-col).abs + (r-row).abs <= 1 ? 1 : 0
end.each_slice(ncols).to_a
  #=> [[0, 0, 1, 0],
  #    [0, 1, 1, 1],
  #    [0, 0, 1, 0],
  #    [0, 0, 0, 0]]

我发现使用Matrix类的方法更容易理解,但可能效率不高。