首页 > 解决方案 > 转换具有变化状态的序列的功能/clojure方式是什么?

问题描述

问题背景与股票交易有关。当进行销售时,我正在尝试更新特定股票的持有量。简化摘录

;; @holdings - an atom
{ "STOCK1" {:trades [Trade#{:id 100 :qty 50}, Trade#{ :id 140 :qty 50}]}
 "STOCK2" ... }

现在给出 的卖出交易Trade{:id 200 :stock "STOCK1", :qty 75},我预计持股会反映

{ "STOCK1" {:trades [Trade#{:id 100 :qty 0}, Trade#{ :id 140 :qty 25}]} }
;; or better drop the records with zero qty.
{ "STOCK1" {:trades [Trade#{ :id 140 :qty 25}]} }

功能性的答案让我望而却步.. 我所看到的只是一个doseq带有原子的循环来保持状态(例如 sale-qty 可能由 1 或 n 次交易满足) - 但它感觉就像 Clojure 中的 C。

有没有更符合clojure的解决方案?地图看起来不合适,因为每个记录处理都需要更新外部状态(待售数量 75 -> 25 -> 0)

免责声明:想学习的 Clojure 新手。

标签: clojure

解决方案


(require '[com.rpl.specter :as s])


(let [stocks     {"STOCK1" {:trades [{:trade/id 100 :trade/qty 50}, {:trade/id 140 :trade/qty 50}]}}
      sale-trade {:trade/id 200 :trade/stock "STOCK1" :trade/qty 75}
      trade-path [(s/keypath (:trade/stock sale-trade) :trades) s/ALL]
      qty-path   (conj trade-path :trade/qty)
      [new-qty _] (reduce (fn [[new-amounts leftover] v]
                              (let [due-amount (min v leftover)]
                                  [(conj new-amounts (- v due-amount)) (- leftover due-amount)]))
                          [[] (:trade/qty sale-trade)]
                          (s/select qty-path stocks))]
    (->> stocks
         (s/setval (s/subselect qty-path) new-qty)
         (s/setval [trade-path #(zero? (:trade/qty %))] s/NONE)))

=> {"STOCK1" {:trades [#:trade{:id 140, :qty 25}]}}

推荐阅读