我正在创建一个二维地图,并希望从预填充空值开始。
我知道以下内容在Elixir中不起作用,但这正是我想要做的。
def empty_map(size_x, size_y) do
map = %{}
for x <- 1..size_x do
for y <- 1..size_y do
map = Map.put(map, {x, y}, " ")
end
end
end
然后我将在地图上绘制形状,如
def create_room(map, {from_x, from_y}, {width, height}) do
for x in from_x..(from_x + width) do
for y in from_x..(from_x + width) do
if # first line, or last line, or first col, or last col
map = Map.replace(map, x, y, '#')
else
map = Map.replace(map, x, y, '.')
end
end
end
end
我曾尝试将其作为2D数组进行操作,但我认为使用坐标作为键的平面地图将更容易使用。
我知道我应该使用递归,但我并不知道如何优雅地进行递归,这种情况不断出现,我还没有看到一种简单/通用的方法来做到这一点。 / p>
答案 0 :(得分:2)
你可以在这里使用两个嵌套的Enum.reduce/3
,将地图作为累加器传递,而不是自己编写递归函数:
defmodule A do
def empty_map(size_x, size_y) do
Enum.reduce(1..size_x, %{}, fn x, acc ->
Enum.reduce(1..size_y, acc, fn y, acc ->
Map.put(acc, {x, y}, " ")
end)
end)
end
end
IO.inspect A.empty_map(3, 4)
输出:
%{{1, 1} => " ", {1, 2} => " ", {1, 3} => " ", {1, 4} => " ", {2, 1} => " ",
{2, 2} => " ", {2, 3} => " ", {2, 4} => " ", {3, 1} => " ", {3, 2} => " ",
{3, 3} => " ", {3, 4} => " "}
答案 1 :(得分:1)
另一种方法是创建一个包含理解的列表元组并将其转换为地图。
iex(18)> defmodule Room do
...(18)> def empty_map(size_x, size_y) do
...(18)> for x <- 1..size_x, y <- 1..size_y do
...(18)> {{x,y}, " "}
...(18)> end
...(18)> |> Enum.into(%{})
...(18)> end
...(18)>
...(18)> def create_room(map, {from_x, from_y}, {width, height}) do
...(18)> last_x = from_x + width
...(18)> last_y = from_y + height
...(18)> for x <- from_x..last_x, y <- from_y..last_y do
...(18)> if x == from_x or x == last_x or y == from_y or y == last_y,
...(18)> do: {{x, y}, "#"}, else: {{x, y}, "."}
...(18)> end
...(18)> |> Enum.into(map)
...(18)> end
...(18)> end
warning: redefining module Room (current version defined in memory)
iex:18
{:module, Room,
<<70, 79, 82, 49, 0, 0, 10, 244, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 1, 10,
131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:create_room, 3}}
iex(19)> Room.empty_map(3,4) |> Room.create_room({1,2}, {3,3})
%{{1, 1} => " ", {1, 2} => "#", {1, 3} => "#", {1, 4} => "#", {1, 5} => "#",
{2, 1} => " ", {2, 2} => "#", {2, 3} => ".", {2, 4} => ".", {2, 5} => "#",
{3, 1} => " ", {3, 2} => "#", {3, 3} => ".", {3, 4} => ".", {3, 5} => "#",
{4, 2} => "#", {4, 3} => "#", {4, 4} => "#", {4, 5} => "#"}
iex(20)>
答案 2 :(得分:1)
使用理解的一行:
for x <- 1..10, y <- 1..10, into: %{}, do: {{x, y}, " "}
答案 3 :(得分:0)
我试图创建自己的解决方案,该解决方案将以一种可以理解但仍尽可能简洁的方式完成上述操作(我自己只是一个初学者)。
def create_room(left_bound, right_bound, lower_bound, upper_bound) do
for x <- left_bound..right_bound,
y <- lower_bound..upper_bound,
into: %{}
do
draw_tile(x, y, border?(x, y, left_bound, right_bound, lower_bound, upper_bound));
end
end
def border?(x, y, left_bound, right_bound, lower_bound, upper_bound) do
x in [left_bound, right_bound] ||
y in [lower_bound, upper_bound]
end
def draw_tile(x, y, _is_border = true) do
{{x,y}, "#"}
end
def draw_tile(x, y, _is_border = false) do
{{x,y}, "."}
end
首先,由于Range
是包容性的(即1..4是[1,2,3,4]而不是[1,2,3]),因此我使用实际边界而不是宽度和高度参数。这使我更加清楚我与代码有什么关系。
接下来,我做了一个简单的理解,如@AA。的答案所示,基本上遍历了x和y的所有可能组合。
如果在该循环的主体内返回一个包含两个元素的元组,则该元组的第一个元素将是键,第二个元素将是插入到映射中的值。
在主体内,我使用draw_tile
,该函数采用坐标和布尔值,指示图块是否为边界图块。
最后,函数border?
仅检查x
或y
是否等于left_bound
或right_bound
和lower_bound
或{{ 1}}。
在Elixir中,尽管存在upper_bound
条语句,但是像在if
中一样,在函数中使用模式匹配更为习惯。
此外,我认为(个人观点,不确定是否在整个Elixir社区中得到反映),应尽可能避免嵌套。
编辑: 您还可以在Elixir文档中找到很多有关语法的信息。