如何在ruby中偏移数组中的值?

时间:2014-05-03 20:12:48

标签: ruby arrays

我正在寻找更好的方法:

我有一个地图数组,例如

location = [1,1,1]
@map[location] = Tile.new

我想找到周围的瓷砖。现在我已经做了以下功能:

def get_surroundings(location, surroundings)
  range = (-1..1).to_a
  range.product(range, range) do |offset|
    surroundings.push(@map[location.map.with_index do |coord, index|
      coord + offset[index]
    end]).compact!
  end
end

它运作得很好,但是:

  • 我几乎无法弄清楚它做了什么,我写了它。
  • 我特别不喜欢offset[index]

我认为最像是location.offset(other_array)。我可以做到,但它会很慢。

3 个答案:

答案 0 :(得分:3)

首先,不要传入一个数组来添加元素,而只是返回正确的元素。改变你的论点只会让以后更难以推理。如果我们将每个步骤分成较小的命名方法,那么事情也会变得更容易阅读,因此我们可以为每个部分添加语义名称:

def offset_location location, offset
  location.zip(offset).map { |a, b| a + b }
end

def surrounding_coordinates location
  offset_values = [-1, 0, 1]
  offsets = offset_values.product(offset_values, offset_values)
  offsets.map do |offset|
    offset_location(location, offset)
  end
end

def surrounding_tiles location
  @map.values_at(surrounding_coordinates(location)).compact
end

答案 1 :(得分:2)

我们定义了一个Location类,它只是Structcoord成员和surroundings方法。 surroundings方法将返回与coord相邻的所有字段(正交或对角)。

# Structs are simply collections of member fields in an object.
class Location < Struct.new(:coord)

  def surroundings

    range = (-1..1).to_a  # This is the same as [-1, 0, 1].

    # By combining the above 'range' with itself three times, we get all
    # possible 3-tuples of -1, 0 and 1 (i.e. the 3-power set of 'range').
    # We then iterate over all these 3-tuples, producing a new array of
    # 3-tuples (which are the neighbours to our 'coord').

    range.product(range, range).map do |offset|
      # One example of offset here is "[-1, 0, 1]".

      # 'transpose' operates on an array of arrays (which can be seen as
      # a two-dimensional matrix of values), and flips rows by columns.
      # For example, this turns [ [-1,1,-1], [0,0,1] ] into [ [-1,0], [1,0], [-1,1] ]
      # In this case, it will pair every coordinate of this location with the
      # corresponding coordinate of the target location (the 'offset').
      #
      # The 'reduce' call simply adds up both values in 'x'.

      [coord, offset].transpose.map {|x| x.reduce(:+)}

      # Effectively, we just did a vector addition of 'coord' and 'offset'.

    end - coord  # We do not include 'coord' in the result.

    # By not including 'coord', we assume that a given location does not 'surround'
    # itself.  This is a matter of definition.

  end
end

>  Location.new([5,5,5]).surroundings
 => [[4, 4, 4], [4, 4, 5], [4, 4, 6], [4, 5, 4], [4, 5, 5], [4, 5, 6], [4, 6, 4], [4, 6, 5], [4, 6, 6], [5, 4, 4], [5, 4, 5], [5, 4, 6], [5, 5, 4], [5, 5, 5], [5, 5, 6], [5, 6, 4], [5, 6, 5], [5, 6, 6], [6, 4, 4], [6, 4, 5], [6, 4, 6], [6, 5, 4], [6, 5, 5], [6, 5, 6], [6, 6, 4], [6, 6, 5], [6, 6, 6]] 

我注意从周围环境中移除坐标。如果这不是您想要的,只需省略- coord声明。

然后,如果您需要地图中的所有对象,则只需使用此方法为您提供的位置对@map进行索引:

@map.find_all {|coord| location.surroundings}

答案 2 :(得分:0)

[编辑:我发现我误解了这个问题。我回答了这个问题:“在三维空间中给定一组整数值坐标,找到所有整数值坐标,每个坐标都是该集合中至少一个坐标的邻居”(适当定义“邻居”)。我的解决方案仍然有效(通过设置my_map => [location]),但可以简化为:

def neighbors(location)
  locs = location.map { |x| [*(x-1..x+1)] }
  locs.shift.product(*locs) - [location]
end  

如果有任何读者对更一般的问题感兴趣,我会留下我的答案。]

我就是这样做的。

<强>代码

def neighbors(my_map)
  my_map.map do |l,_|
    locs = location.map { |x| [*(x-1..x+1)] }
    locs.shift.product(*locs)
  end.reduce(:|) - my_map.keys
end

示例

my_map = { [1,2,3]=>"123", [2,3,2]=>"232", [1,1,2]=>"112" }
neighbors(my_map)
  #=> [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 2], [0, 2, 3], [0, 2, 4],
  #    [0, 3, 2], [0, 3, 3], [0, 3, 4], [1, 1, 3], [1, 1, 4], [1, 2, 2],
  #    [1, 2, 4], [1, 3, 2], [1, 3, 3], [1, 3, 4], [2, 1, 2], [2, 1, 3],
  #    [2, 1, 4], [2, 2, 2], [2, 2, 3], [2, 2, 4], [2, 3, 3], [2, 3, 4],
  #    [1, 2, 1], [1, 3, 1], [1, 4, 1], [1, 4, 2], [1, 4, 3], [2, 2, 1],
  #    [2, 3, 1], [2, 4, 1], [2, 4, 2], [2, 4, 3], [3, 2, 1], [3, 2, 2],
  #    [3, 2, 3], [3, 3, 1], [3, 3, 2], [3, 3, 3], [3, 4, 1], [3, 4, 2],
  #    [3, 4, 3], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 1, 1], [0, 2, 1],
  #    [1, 0, 1], [1, 0, 2], [1, 0, 3], [1, 1, 1], [2, 0, 1], [2, 0, 2],
  #    [2, 0, 3], [2, 1, 1]]

my_map arr的三个键发现总共有56个唯一的邻居(可能总共3 ** 3 - 3 = 78,'-3'以避免计算{的元素{1}})。

<强>解释

假设my_map与上例相同。 (我给出的哈希值是任意的。)

我们将my_map的三个键/值对中的每一个映射到一个相邻单元格的数组中,取这些数组的并集,最后从联合中删除哈希键。

第一个值my_map传递到其块中:

map

通常,此数组的每个元素(对应于哈希元素的键和值)将由块变量表示,但由于我们不使用值([[1,2,3], "123"] ),我已替换它的变量带有下划线。密钥"123"被分配给块变量[1,2,3]

接下来我们有

l

然后

locs = location.map { |x| [*(x-1..x+1)] }
  #=> [1,2,3].map { |x| [*(x-1..x+1)] }
  #=> [[0, 1, 2], [1, 2, 3], [2, 3, 4]] 

所以现在

a = locs.shift #=> [0, 1, 2]

意味着locs #=> [[1, 2, 3], [2, 3, 4]] 的邻居是:

[1,2,3]

请注意b = a.product(*locs) #=> [0, 1, 2].product([1, 2, 3], [2, 3, 4]) #=> [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 2], [0, 2, 3], [0, 2, 4], # [0, 3, 2], [0, 3, 3], [0, 3, 4], [1, 1, 2], [1, 1, 3], [1, 1, 4], # [1, 2, 2], [1, 2, 3], [1, 2, 4], [1, 3, 2], [1, 3, 3], [1, 3, 4], # [2, 1, 2], [2, 1, 3], [2, 1, 4], [2, 2, 2], [2, 2, 3], [2, 2, 4], # [2, 3, 2], [2, 3, 3], [2, 3, 4]] 在此数组中,但不是它自己的邻居。我会在最后删除它。 ([1,2,3]的另外两个元素也在这个数组中,因此需要删除,但现在没有必要这样做,因为my_map的其他两个元素也都有所有元素在相应的数组中。)

我们对my_map的其他两个元素重复此操作,获取我将表示my_mapc的数组。

我们想要这三个数组的联合:

d

将消除重复(并保留顺序)。我们使用Enumerable#reduceArray#|执行此操作:

(b | c | d)

最后,我们使用Array#-删除e = [b,c,d].reduce(:|) 中的元素。

my_map

<强>效率

如果e - my_map.keys 很大,将27个相邻元素的每个数组转换为一个集合可能会更有效率。