迷宫生成算法的惯用Clojure实现

迷宫生成算法的惯用Clojure实现,clojure,Clojure,我正在用Python和Clojure实现创建和解决迷宫的算法。我有Python方面的经验,正在努力学习Clojure。我可能做了太多从Python到Clojure的转换,我正在寻找一种更惯用的方式来实现Clojure中的代码 首先是工作的Python实现 import random N, S, E, W = 1, 2, 4, 8 DX = {E: 1, W: -1, N: 0, S: 0} DY = {E: 0, W: 0, N: -1, S: 1} OPPOSITE = {E: W, W:

我正在用Python和Clojure实现创建和解决迷宫的算法。我有Python方面的经验,正在努力学习Clojure。我可能做了太多从Python到Clojure的转换,我正在寻找一种更惯用的方式来实现Clojure中的代码

首先是工作的Python实现

import random

N, S, E, W = 1, 2, 4, 8
DX = {E: 1, W: -1, N: 0, S: 0}
DY = {E: 0, W: 0, N: -1, S: 1}
OPPOSITE = {E: W, W: E, N: S, S: N}


def recursive_backtracker(current_x, current_y, grid):
    directions = random_directions()
    for direction in directions:
        next_x, next_y = current_x + DX[direction], current_y + DY[direction]
        if valid_unvisited_cell(next_x, next_y, grid):
            grid = remove_walls(current_y, current_x, next_y, next_x, direction, grid)
            recursive_backtracker(next_x, next_y, grid)
    return grid


def random_directions():
    directions = [N, S, E, W]
    random.shuffle(directions)
    return directions


def valid_unvisited_cell(x, y, grid):
    return (0 <= y <= len(grid) - 1) and (0 <= x <= len(grid[y]) - 1) and grid[y][x] == 0


def remove_walls(cy, cx, ny, nx, direction, grid):
    grid[cy][cx] |= direction
    grid[ny][nx] |= OPPOSITE[direction]
    return grid
随机导入
N、 S,E,W=1,2,4,8
DX={E:1,W:-1,N:0,S:0}
DY={E:0,W:0,N:-1,S:1}
相反={E:W,W:E,N:S,S:N}
def递归_回溯器(当前_x、当前_y、网格):
方向=随机方向()
对于方向中的方向:
next_x,next_y=当前_x+DX[方向],当前_y+DY[方向]
如果有效的\u未访问的\u单元格(下一个\u x,下一个\u y,网格):
栅格=移除墙壁(当前y、当前x、下y、下x、方向、栅格)
递归回溯(下一个x,下一个y,网格)
回流栅
def random_directions():
方向=[N,S,E,W]
随机。洗牌(方向)
返回方向
def有效未访问单元格(x、y、栅格):

return(0这似乎是一个基本合理的Python代码到Clojure的翻译(包括一些初学者经常错过的东西-做得很好)…直到我们进入问题的核心
递归回溯程序
。你不能在这里直接翻译Python,因为你的算法假设
网格
是可变的:你在
for
循环中递归调用自己四次,你需要对网格所做的更改得到反映。Clojure不是这样工作的,所以这一切都不起作用。您得到的实际错误是一个不相关的语法错误(仔细检查循环/重现的接口),但它在这里并不相关,因为您无论如何都必须重写函数,所以我就不谈了

现在,如何在不改变网格的情况下重写此函数呢?通常情况下,您可以使用
reduce
:对于四个方向中的每一个,您调用
递归回溯程序
,获取修改后的网格,并确保在下一次递归调用中使用修改后的网格。概述如下:像这样:

(defn recursive-backtracker
  [current-x current-y grid]
  (reduce (fn [grid direction]
            (let [next-x (+ current-x (DX direction))
                  next-y (+ current-y (DY direction))]
              (if (valid-unvisited-cell? next-x next-y grid)
                (recursive-backtracker next-x next-y
                                       (remove-walls current-x current-y next-x next-y
                                                     direction grid))
                grid)))
          grid, (clojure.core/shuffle [:N :S :E :W])))

根据该定义,
(递归回溯0(生成空网格5))
生成
[[2 5 6 3 5][4 10 9 6 9][14 3 1 12 4][12 6 3 9 12][10 11 3 9]]
-这是一个有效的迷宫吗?看起来不错,但我不知道。你可能也不知道。这让我想到了另一点:使用整数和位运算是一个无意义优化的练习。相反,让网格中的每个条目都是一个地图或集合,其中包含关键字,说明它的开放方向。然后在检查时,你可以至少可以大致了解迷宫是否自洽。

顺便说一句,您可以将x/y对作为实际对象,而不是两个不同的数字来处理,从而在这里做得更好。例如,如果
pos
[4 3]
dir
[0-1]
,则
(map+pos dir)
产生
[4 2]
。比在所有地方分别管理x和y容易得多。利用位字段存储每个单元格的信息,这对于不必要的优化是有效的。回答非常好,反馈非常有用。FWIW,Clojure编程书在第3章中有一个迷宫生成示例。代码在这里,但是没有这本书的文本就没有意义。
(defn recursive-backtracker
  [current-x current-y grid]
  (reduce (fn [grid direction]
            (let [next-x (+ current-x (DX direction))
                  next-y (+ current-y (DY direction))]
              (if (valid-unvisited-cell? next-x next-y grid)
                (recursive-backtracker next-x next-y
                                       (remove-walls current-x current-y next-x next-y
                                                     direction grid))
                grid)))
          grid, (clojure.core/shuffle [:N :S :E :W])))