背景

初学clojure,想着看一些算法来熟悉clojure语法及相关算法实现。

找到一个各种语言生成迷宫的网站:http://rosettacode.org/wiki/Maze_generation

在上述网站可以看到clojure的实现版,本文就是以初学者的视角解读改程序。

小试牛刀

先看一些简单的示例,以帮助我们理解迷宫生成程序。

绑定符号x++

  1. (defn f [x]
  2. (let [x++ (+ x 5)]
  3. #{[x x++]}))
  4. (println (f 1))
  5. => #'sinlov.clojure.base-learn/f
  6. #{[1 6]}
  7. => nil

Tips: 上述程序将x++绑定为x+5,不同于c语言中的自增运算符。

集合过滤

  1. (select odd? (set [1 2 3 4 5]))
  2. => #{1 3 5}
  3. (select (partial odd?) (set [1 2 3 4 5]))
  4. => #{1 3 5}

select语法参考文档:http://clojuredocs.org/clojure.set/select

partial解释见下文

vec交叉合并

  1. (interleave [0 1 2] ['a 'b 'c])
  2. => (0 a 1 b 2 c)
  3. (interleave [0 1 2] ['a 'b 'c] ['b])
  4. => (0 a b)
  5. (interleave [0 1 2] ['a 'b 'c] (repeat 'z))
  6. => (0 a z 1 b z 2 c z)

文档:http://clojuredocs.org/clojure.core/interleave

transduce

transducer是clojure里面的一种编程思想,使用transducer可以简化很多语法。

可以参考这篇文章链接,帮助理解

文档:http://clojuredocs.org/clojure.core/transduce

思路

笔者阅读了迷宫生成算法,将思路整理如下

坐标点与符号映射关系

比如迷宫的左上角是如何生成的,不同大小的迷宫如何确定?

经过阅读源码发现,一个坐标点的符号与其周围4个临接点相关,如果按照坐标点表示,5个点排序顺序是一致的。

比如,上述坐标点(5,5),和其4个临界点。可以看到在该坐标系内,一个点与其临界点做成的集合排序一定是下面的顺序:

比如迷宫左上角坐标是(0, 0),该点五元组应该是

  1. 不在迷宫,不在迷宫,(0, 0), (0, 1), (1, 0)

假设不在迷宫或者该位置为空,记为0;如果是墙记为1

那么上述五元组可以换算为11100

再比如迷宫右上角,五元组为

  1. (n-1, 0), 不在迷宫, (n, 0), (n, 1), 不在迷宫

可换算为10110

按照如上规则可以生成如下表:

  1. [" " " " " " " " "· " "╵ " "╴ " "┘ "
  2. " " " " " " " " "╶─" "└─" "──" "┴─"
  3. " " " " " " " " "╷ " "│ " "┐ " "┤ "
  4. " " " " " " " " "┌─" "├─" "┬─" "┼─"]

程序代码

  1. (ns maze.core
  2. (:require [clojure.set :refer [intersection
  3. select]]
  4. [clojure.string :as str]))
  5. ;; 得到周围临界点
  6. (defn neighborhood
  7. ([] (neighborhood [0 0]))
  8. ([coord] (neighborhood coord 1))
  9. ([[y x] r]
  10. (let [y-- (- y r) y++ (+ y r)
  11. x-- (- x r) x++ (+ x r)]
  12. #{[y++ x] [y-- x] [y x--] [y x++]})))
  13. ;; 判断位置是否为空
  14. (defn cell-empty? [maze coords]
  15. (= :empty (get-in maze coords)))
  16. ;; 判断位置是否为墙
  17. (defn wall? [maze coords]
  18. (= :wall (get-in maze coords)))
  19. ;; 过滤迷宫中指定类型的点的集合
  20. (defn filter-maze
  21. ([pred maze coords]
  22. (select (partial pred maze) (set coords)))
  23. ([pred maze]
  24. (filter-maze
  25. pred
  26. maze
  27. (for [y (range (count maze))
  28. x (range (count (nth maze y)))]
  29. [y x]))))
  30. ;; 创建新迷宫
  31. (defn create-empty-maze [width height]
  32. (let [width (inc (* 2 width))
  33. height (inc (* 2 height))]
  34. (vec (take height
  35. (interleave
  36. (repeat (vec (take width (repeat :wall))))
  37. (repeat (vec (take width (cycle [:wall :empty])))))))))
  38. (defn next-step [possible-steps]
  39. (rand-nth (vec possible-steps)))
  40. ;; 核心算法,深度优先递归
  41. (defn create-random-maze [width height]
  42. (loop [maze (create-empty-maze width height)
  43. stack []
  44. nonvisited (filter-maze cell-empty? maze)
  45. visited #{}
  46. coords (next-step nonvisited)]
  47. (if (empty? nonvisited)
  48. maze
  49. (let [nonvisited-neighbors (intersection (neighborhood coords 2) nonvisited)]
  50. (cond
  51. (seq nonvisited-neighbors)
  52. (let [next-coords (next-step nonvisited-neighbors)
  53. wall-coords (map #(+ %1 (/ (- %2 %1) 2)) coords next-coords)]
  54. (recur (assoc-in maze wall-coords :empty)
  55. (conj stack coords)
  56. (disj nonvisited next-coords)
  57. (conj visited next-coords)
  58. next-coords))
  59. (seq stack)
  60. (recur maze (pop stack) nonvisited visited (last stack)))))))
  61. ;; 迷宫坐标与字符映射
  62. (def cell-code->str
  63. [" " " " " " " " "· " "╵ " "╴ " "┘ "
  64. " " " " " " " " "╶─" "└─" "──" "┴─"
  65. " " " " " " " " "╷ " "│ " "┐ " "┤ "
  66. " " " " " " " " "┌─" "├─" "┬─" "┼─"])
  67. ;; 获取迷宫坐标的类型
  68. ;; 使用5 bit表示一个点对应的字符映射
  69. ;; 例如:00111对应┘
  70. (defn cell-code [maze coord]
  71. (transduce
  72. (comp
  73. (map (partial wall? maze))
  74. (keep-indexed (fn [idx el] (when el idx)))
  75. (map (partial bit-shift-left 1)))
  76. (completing bit-or)
  77. 0
  78. (sort (cons coord (neighborhood coord)))))
  79. (defn cell->str [maze coord]
  80. (get cell-code->str (cell-code maze coord)))
  81. ;; 将迷宫坐标转换为字符
  82. (defn maze->str [maze]
  83. (->> (for [y (range (count maze))]
  84. (for [x (range (count (nth maze y)))]
  85. (cell->str maze [y x])))
  86. (map str/join)
  87. (str/join \newline)))
  88. ;; 生成迷宫
  89. (println (maze->str (create-random-maze 10 10)))

上述程序输出:

Clojure——学习迷宫生成的更多相关文章

  1. ICML 2018 | 从强化学习到生成模型:40篇值得一读的论文

    https://blog.csdn.net/y80gDg1/article/details/81463731 感谢阅读腾讯AI Lab微信号第34篇文章.当地时间 7 月 10-15 日,第 35 届 ...

  2. O(n)线性空间的迷宫生成算法

    之前所有的迷宫生成算法,空间都是O(mn),时间同样是O(mn),时间上已经不可能更优化, 于是,我就从空间优化上着手,研究一个仅用O(n)空间的生成算法. 我初步的想法是,每次生成一行,生成后立即输 ...

  3. NASNet学习笔记——   核心一:延续NAS论文的核心机制使得能够自动产生网络结构;    核心二:采用resnet和Inception重复使用block结构思想;    核心三:利用迁移学习将生成的网络迁移到大数据集上提出一个new search space。

    from:https://blog.csdn.net/xjz18298268521/article/details/79079008 NASNet总结 论文:<Learning Transfer ...

  4. [迷宫中的算法实践]迷宫生成算法——递归分割算法

    Recursive division method        Mazes can be created with recursive division, an algorithm which wo ...

  5. MySQL学习记录--生成时间日期数据

    时间数据格式组件: 组件 定义 范围 YYYY 年份,包括世纪 1000~9999 MM 月份 01(January)~12(December) DD 日 01~31 HH 小时 00~23 HHH ...

  6. Clojure学习笔记(一)——介绍、安装和语法

    什么是Clojure Clojure是一种动态的.强类型的.寄居在JVM上的语言. Clojure的特性: 函数式编程基础,包括一套性能可以和典型可变数据结构媲美的持久性数据结构 由JVM提供的成熟的 ...

  7. [ExtJS5学习笔记]第三节 sencha cmd学习笔记 生成应用程序构建的内部细节

    本文地址: http://blog.csdn.net/sushengmiyan/article/details/38316829本文作者:sushengmiyan------------------- ...

  8. Clojure学习资料

    以下大部分收藏自博客:http://blog.csdn.net/ithomer/article/details/17225813 官方文档: http://clojure.org/documentat ...

  9. Clojure 学习入门(19)—— 数组

    1.创建数组 1.1 从集合创建数组 into-array into-array (into-array aseq) (into-array type aseq) 演示样例: user=> (i ...

随机推荐

  1. Android-Error3:Error when loading the SDK

    解决方法: 用C:\android\sdk\tools中的devices.xml将出现错误的地方的devices.xml替换掉既可以了.

  2. 高阶自定义View --- 粒子变幻、隧道散列、组合文字

    高阶自定义View --- 粒子变幻.隧道散列.组合文字 作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:h ...

  3. ajax请求成功前loading

    1.jquery方式 <!DOCTYPE html><html lang="en"><head> <meta charset=" ...

  4. Go数组、切片、映射的原理--简明解析

    数组.切片.映射是Golang的最重要的数据结构,下面是对这3种数据结构的一点个人总结: 一.数组 数组是切片和映射的基础数据结构. 数组是一个长度固定的数据类型,存储着一段具有相同数据类型元素的连续 ...

  5. Windows系统安装Azure CLI

    本文将介绍在Windos系统下如下安装CLI 1.打开Azure官方链接:https://www.azure.cn/downloads/ 2.按照向导进行安装 3.打开Windows Powershe ...

  6. poj 3592 缩点+SPFA

    题意:给出一个矩阵,其中#代表墙,不可走,0-9代表权值,*代表可以选择传送.求从0,0点开始出发能获得最大权值. 思路:因为*的出现会有环的情况,先建图连边,将环进行Tarjan缩点,之后再从0,0 ...

  7. 自制tunnel口建虚拟专网实验

    R1: interface Tunnel12 ip address 192.168.12.1 255.255.255.0 ip ospf 1 area 0 tunnel source 123.123. ...

  8. 201521123051 《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 使用工具:百度脑图 1.2 使用常规方法总结其他上课内容.(多态) 多态的定义:父类的引用指向子类的对象. 父类的引用:一是指父类变 ...

  9. 201521123059 《Java程序设计》第一周学习总结

    第一周学习总结 1.本周学习总结 开学第周我们开始了JAVA学习,刚开始还有点懵,连熟悉相关的软件都要摸索很久,最后在看老师给的一些教程和课本的情况下安装了JDK和eclipse等软件,并进行了一次实 ...

  10. 201521123018 《Java程序设计》第13周学习总结

    1. 本章学习总结 2. 书面作业 一.1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn,分析返回结果有何不同?为什么会有这样的不同? 返回时间 ...