【问题标题】:Clojure re-order functionClojure 重排序函数
【发布时间】:2018-09-29 18:32:00
【问题描述】:

我必须承认,这仍然是我作为 clojure 新手的地方。我经常发现,如果我在 Clojure Docs 中搜索,我会找到我正在寻找的函数。 ;)

但我对此感到紧张,但也许我会走运。

我有一个纸牌游戏。每个玩家的手牌都在 1 到 9 张之间。

通过抽牌从他们的牌组顶部一次将一张牌放入他们的手中。

玩家所要求的是能够将 UNORGANIZED 手牌或 UNSORTED 手牌带到那里并重新组织他们的手牌。

我提供了一个解决方案“如何在命令窗口中使用 /re-order 31487652 之类的命令,它可以发出函数(不用担心命令,它只是排序函数)。

这样做的目标是拿到他们手中的每张卡片 12345678 并将顺序更改为他们提供的新顺序,即 31487652。

数据格式如下:

(:hand player)

[{:name : "Troll", :_id : 39485723},
{:name : "Ranger", :_id : 87463293},
{:name : "Archer", :_id : 78462721},
{:name : "Orc", :_id : 12346732},
{:name : "Orc", :_id : 13445130},
{:name : "Spell", :_id : 23429900},
{:name : "Dagger", :_id : 44573321}]

我唯一的问题是,我可以使用传统的编程语言来思考这个问题,我的意思是很简单,你只需将数据复制到另一个数组,哈哈,但我的意思是我们不喜欢 clojure 吗?...

但我想保留纯粹的 clojure 意识形态,并了解如何做这样的事情。我的意思是,如果它只是“使用这个函数”,那我想很好,但我不想创建一个原子,除非是强制性的,但我不认为是这样。

如果有人可以帮助我开始思考使用 clojure 解决此问题的方法,那就太棒了!

感谢任何帮助/建议/回答...

附录#1

(defn vec-order [n]
  (into [] (if (pos? n)
             (conj (vec-order (quot n 10)) (mod n 10) )
             [])))

(defn new-index [index new-order] (.indexOf new-order (inc index)))

(defn re-order [state side value]
  (println (get-in @state [side :hand]))
  (update @state [side :hand]
          (fn [hand]
            (->> hand
                 (map-indexed (fn [index card] [(new-index index (vec-order value)) card]))
                 (sort-by first)
                 (mapv second))))
  (println (get-in @state [side :hand])))

这是我当前的代码,用于提取数据。有一个巨大的@state,玩家所在的一边。我用:

(println (get-in @state [side :hand]))

查看执行defn之前和之后的数据,但我没有得到任何更改。为简单起见,向量是 21436587 到 [2 1 4 3 6 5 8 7]。

但是我遗漏了一些东西,因为我什至运行了 /re-order 12345678 以确保没有移动任何东西,而我只是没有看到任何东西。但是什么都没有……

谢谢你,让我走到这一步。

【问题讨论】:

    标签: sorting clojure reorganize


    【解决方案1】:

    如果您将所需的元素顺序作为向量,您可以sort-by 通过返回该向量中卡片索引的函数:

    (let [cards [1 2 3 4 5 6 7 8]
          my-order [3 1 4 8 7 6 5 2]]
      (sort-by #(.indexOf my-order %) cards))
    ;; => (3 1 4 8 7 6 5 2)
    

    【讨论】:

    • Clojure 优雅 :-)
    【解决方案2】:

    因此,注意的第一个函数将是update,如果我们这样调用它,它将允许我们返回一个新玩家,该函数应用于手牌。

    (update player :hand (fn [hand] ... ))
    

    一旦我们有了这个基本结构,下一个可以帮助我们的函数是map-indexed,它允许我们将当前手牌与新的排序索引配对。

    从那里,我们将能够sort-by 索引,最后mapv 检索卡片。

    因此,最终的结构将如下所示:

    (defn sort-hand [player new-order]
      (update
        player
        :hand
        (fn [hand]
         (->> hand 
              (map-indexed (fn [index card] [(new-index index new-order) card]))
              (sort-by first)
              (mapv second)))))
    

    为此,预计new-order 是一个类似于[3 1 4 8 7 6 5 2] 的向量

    至于new-index的解决方案,

    我们可以像这样使用.indexOf (defn new-index [index new-order] (.indexOf new-order (inc index)))

    【讨论】:

    • 我弄错了,它们应该是键,比如:name和:_id
    • 既然您正在使用原子,请考虑使用交换! (clojuredocs.org/clojure.core/swap!)
    • 明白了,是的,我总体上使用了原子!
    【解决方案3】:

    在您的帮助下:

    (defn vec-order [n]
      (into [] (if (pos? n)
                 (conj (vec-order (quot n 10)) (mod n 10) )
                 [])))
    
    (defn new-index [new-order index] (.indexOf new-order (inc index)))
    
    (defn re-order [state side value]
      (swap! state update-in [side :hand]
                 (fn [hand]
                   (->> hand
                        (map-indexed (fn [index card] [(new-index (vec-order value) index) card]))
                        (sort-by first)
                        (mapv second)))))
    

    工作! 100%

    【讨论】:

    • 我回答的重点是您根本不必使用map-indexedmapv。您当前的解决方案将创建一个(new index, card) 元组序列,只是为了使用它的第一个元素将其提供给sort-by,然后使用(mapv second) 将其删除。相反,您可以直接使用(sort-by #(new-index % new-order) hand)
    • 我还将您的 new-index 函数中的参数顺序更改为 [new-order index]。通过此更改,您可以使用 partial 轻松获得一个新函数,该函数将为您提供特定订单的索引:(partial new-index new order)
    • 不要将交换放入函数中,这样就可以了。将其作为域代码的一部分保留(更容易测试,更容易重写)。在需要的地方进行交换(例如,re-order! 使用应用的全局状态)
    猜你喜欢
    • 2013-03-23
    • 2014-10-30
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    • 1970-01-01
    • 2018-05-26
    • 2018-06-04
    • 1970-01-01
    相关资源
    最近更新 更多