首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Clojure中,如何合并两个映射向量?

在Clojure中,如何合并两个映射向量?
EN

Stack Overflow用户
提问于 2014-05-22 12:22:38
回答 1查看 1.7K关注 0票数 0

我有一个实体向量(映射),我已经将这个向量过滤到那些具有特定键的实体。然后,我应用一个函数来更新这些实体中的一些值,现在有了一个子集,我想将它合并到原始的超集中。

代码语言:javascript
复制
world     [{a} {b} {c} {d} {e}]
a         [{b} {d} {e}]            ; (filter matches? world)
b         [{b'} {d'} {e'}]         ; (map func a)
new-world [{a} {b'} {c} {d'} {e'}] ; (merge world b)

如何将更新b与原始world合并

我的地图结构是:

代码语言:javascript
复制
{:id identity
 :attrs {:property1 {:param1 value
                     :param2 value}
         :property2 {}}

可以有可变数量的属性。属性可以有0 ({})或更多的params。地图中唯一正在变化的部分是值,而且只有其中的一部分。

我必须窥视每一张地图来识别它,然后进行相应的合并?做这件事的惯用方法是什么?

Clojure的映射函数可以接受多个序列,但不会将我的子集b与整个超集world (只有b数量的世界实体)进行比较,并在b耗尽后停止。

代码语言:javascript
复制
> (map #(str %1 " " %2) [1 2 3 4 5] [6 7 8])
("1 6" "2 7" "3 8")

首先,我是否为数据选择了错误的结构?如果没有矢量和一张地图,我会更好吗?

代码语言:javascript
复制
{:entid1
   {:property1 {:param1 value :param2 value}
    :property2 {}}
 :entid2
   {:property1 {:param1 value :param2 value}
    :property2 {:param1 value}
    :property3 {:param1 value :param2 value :param3 value}}}

这个问题看起来很相似,但没有正确地合并我的向量。

真实世界实现

我的实际代码是我写的游戏的一部分,以熟悉Clojure (本例中的ClojureScript)。

游戏状态(世界)如下:

代码语言:javascript
复制
[{:id :player,
  :attrs
    {:gravity {:weight 10},
     :jump {:height 60, :falling false, :ground true},
     :renderable {:width 37, :height 35},
     :position {:x 60, :y 565},
     :walk {:step 4, :facing :right},
     :input {},
     :solid {:blocked false}}}
 {:id wall1,
  :attrs
    {:solid {},
     :position {:x 0, :y 0},
     :renderable {:width 20, :height 600}}}
 {:id wall2,
  :attrs
    {:solid {},
     :position {:x 780, :y 0},
     :renderable {:width 20, :height 600}}}
 {:id platform3,
  :attrs
    {:solid {},
     :position {:x 20, :y 430},
     :renderable {:width 600, :height 20}}}] 

我更新每个动画帧上的世界,然后重新渲染画布。

代码语言:javascript
复制
(defn game-loop []
  (ui/request-frame game-loop)
  (-> world
      system/update!
      ui/render))

system/update!是:

代码语言:javascript
复制
(defn update! [world]
  (let [new-world (->> @world
                      (phys/move @player/input-cmd))]
    (player/clear-cmd)
    (reset! world new-world)
    @world))

我的计划是在最后一个宏线程中有一系列更新世界的系统。我正在编写phys/move

这意味着系统必须更新世界,而不是仅仅返回它们对世界的影响(变化的矢量)。

我正在考虑让system/update!函数管理对世界的影响(应用更新)是否更容易管理。因此,系统只返回它们的更改列表。类似于:

代码语言:javascript
复制
(defn update! [world]
  (let [updates []
        game @world]
     (conj updates (phys/move @player/input-cmd game))
     (conj updates (camera/track :player game))
     ; etc.
     (reset! world
       (map #(update-world updates %) world))
     @world))

然而,重复的(conj updates (func world))感觉很笨重。这可能不仅仅是在系统函数中合并更新(或返回修改后的实体)。

如何优雅地传递我的系统功能(phys/movecamera/track)和子系统函数(walkjump)之间的状态更改?

我尝试将Joaquin的map仅应用于我的move系统:

代码语言:javascript
复制
(ns game.phys)

(def step 4)
(def height 50)

(defn walk?
  "is this a walk command?"
  [cmd]
  (#{:left :right} cmd))

(defn walks?
  "update? equivalent"
  [entity]
  (contains? (:attrs entity) :position))

(defn walk
  [cmd entity]
  (if (walks? entity)
    (let [x (get-in entity [:attrs :position :x]
          op (if (= cmd :left) - +)
          update {:attrs {:position {:x (op x step)}
                          :walk     {:facing cmd}}}]
      (merge entity update))
    entity))

; cmd is a deref'ed atom that holds player input commands
; e.g. :left :right: :jump
(defn move
  [cmd world]
  (cond
    (walk? cmd) (map #(walk input-cmd %) world)
    (jump? cmd) (map #(jump height %) world)
    :else world))
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-05-23 07:04:34

它比所有这些都简单(有一个地图向量是相当常见的事情,它可能适合你的问题)。与其做一个过滤器,然后做一个映射,只需做一个映射:

代码语言:javascript
复制
(defn update? [entity] ...)
(defn update-entity [entity] ...)

(defn update-world [world]
  (let [update #(if (matches? %) (update-entity %) %)]
    (map update world)))

这种更新或只保留它的模式是很常见的,让更新某个东西的函数返回更新的东西,或者如果它什么也不做的话返回旧的东西,这是惯用的做法,所以在最后它应该是这样的:

代码语言:javascript
复制
(defn update-entity?
  "Predicate that returns if an entity needs updating"
  [entity] ...)

(defn update-entity
  "Updates an entity if it is needed.
   Otherwise returns the unchanged entity"
  [entity]
  (if (update-entity? entity)
    (assoc entity :something :changed)
    entity))

(defn update-world [world]
  (map update-entity world))

您可以在这个蛇游戏的游戏逻辑中看到这种行为的一些例子:

https://github.com/joakin/cnake/blob/master/src/cnake/game.cljs#L54-L66

https://github.com/joakin/cnake/blob/master/src/cnake/game.cljs#L102-L114

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23806687

复制
相关文章

相似问题

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