首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >习服?表演和功能足够吗?

习服?表演和功能足够吗?
EN

Code Review用户
提问于 2012-12-29 17:39:20
回答 1查看 453关注 0票数 3

问题

免责声明:这是我的第一个clojure函数&我仍在忙着完成“编程Clojure”的最后几章。谢谢!:)

我正在编写一个函数来随机填充游戏地图中的部分(也就是说,这不是一个可以递归填充所有可用空间的传统洪水填充)。这个想法是创建一个随机形状的岛屿,在这个岛内创建一个随机形状的山。(也就是说,将一组岛屿点作为过滤器传递到下一个洪水填充函数,这样,该山将只在岛上的瓷砖中创建)。

这就是我的代码目前产生的结果(您会注意到PragProg的编程Clojure中从蛇游戏中窃取的窗口/绘图代码)。(;)

Clojure样式

这是我的密码:

代码语言:javascript
复制
(def dirs2 '([-1 0] [1 0] [0 -1] [0 1]))

(defn neighbours [pt]
  (for [dir dirs2]
    (vec (map + pt dir))))

(defn flood-fill2 
  "start -> point to start filling from
   n -> number of tiles to create
   filter-fn -> a fn that must return true for new points"
    ([start n]
      (flood-fill2 start n (fn [& _] true)))
    ([start n filter-fn]
     (loop [result #{}
      candidates #{start}
      i n]
      (if (or (zero? i) (empty? candidates))
        result 
        (let [current (rand-nth (seq candidates))]
          (if (and (filter-fn current) (not (result current)))
            (recur 
              (conj result current) 
              (apply conj (disj candidates current) (neighbours current))
              (dec i))
            (recur result (disj candidates current) i)))))))
  • 这是熟能生巧的code代码吗?其中的循环构造感觉就像我学会用Java / C++编写的标准迭代代码。你看到写这个的方法更短了吗?
  • 我能以某种方式实现这一点吗?(take 600 (lazy-fill [25 25] optional-src-set))看起来比(flood-fill2 [25 25] 600 island)酷多了。
  • 我尝试编写一个匿名函数,使用这个表示法( #() )返回任何参数(S)的true,但最终不得不放弃,使用fn(fn [& _] true)编写它。我能用#()吗?

性能

我的初始(天真)实现如下所示:

代码语言:javascript
复制
(defn flood-fill [filled n]
  (if (zero? n)
    filled
    (let [start-point (rand-nth filled)
      next-point (rand-nth (neighbours start-point))] 
      (if (contains? (set filled) next-point)
        (recur filled n)
        (recur (cons next-point filled) (dec n))))))

但是:

代码语言:javascript
复制
user=> (time (flood-fill [[25 25]] 1250))
(time (flood-fill [[25 25]] 1250))
"Elapsed time: 8430.56 msecs"
([8 18] [43 40] [23 9] [38 40] ...

我的flood-fill2做得更好:

代码语言:javascript
复制
user=> (time (flood-fill2 [25 25] 1250))
(time (flood-fill2 [25 25] 1250))
"Elapsed time: 257.361 msecs"
#{[34 33] [35 34] [36 3] [36 35] [38 5] [10 9] ...

但是使用(time (flood-fill2 [500 500] 500000))来填充1000 *1000个瓷砖地图的50%要花费很长时间(@ 100% CPU)。最后我杀了那个REPL。

有什么表演建议吗?我试图避免从集合到向量/列表的许多转换,但是rand-nth需要一个序列,我找不到一个很好的“随机集元素”函数。

谢谢你看了这么多。

EN

回答 1

Code Review用户

发布于 2013-03-16 06:21:43

我自己也是clojure的一名熟练工人,我不知道你所有的问题都有答案,但我有一些意见。

我不确定我完全理解你的算法,但是看起来你真的有两个循环。你有一个内环,寻找下一个可以绘制的瓷砖(不包括i),而外部循环实际上是填充像素,从n开始计数i。如果你以某种方式将这两者分开,可能会更清楚。

因为你随机选择候选人,所以填充n块所需的时间是随机的。

我发现当我这样做的时候:

代码语言:javascript
复制
(loop [result ()
       input input-seq]
    (let [item (computation input)]
        (recur (conj result item) (rest input)))

它通常可以改写为:

代码语言:javascript
复制
(reduce computation input-seq)

(但我无法立即看出这是否适用于您的功能)

您考虑过使用一个2d的tiles数组而不是一个坐标列表来实现这一点吗?

其他几个问题:

  • 您可以创建一个始终使用:(不断为true)返回true的函数。
  • 我非常喜欢使用( (map +))进行矢量加法,但您可以使用(mapv)代替(向量(map))。
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/20028

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档