我正在寻找更好的方法:
我有一个地图数组,例如
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)
。我可以做到,但它会很慢。
答案 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
类,它只是Struct
个coord
成员和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_map
和c
的数组。
我们想要这三个数组的联合:
d
将消除重复(并保留顺序)。我们使用Enumerable#reduce和Array#|执行此操作:
(b | c | d)
最后,我们使用Array#-删除e = [b,c,d].reduce(:|)
中的元素。
my_map
<强>效率强>
如果e - my_map.keys
很大,将27个相邻元素的每个数组转换为一个集合可能会更有效率。