我有两个载体
(def container [{:no 1 :volume 10} {:no 2 :volume 20}])
(def cans [{:no 1 :volume 2} {:no 2 :volume 8} {:no 1 :volume 5} {:no 2 :volume 8}])
我想用罐子装满容器,以便返回这样的东西:
[{:no 1 :volume 10
:cans [{:no 1 :volume 2} {:no 2 :volume 8}]}
{:no 2 :volume 20
:cans [{:no 1 :volume 5} {:no 2 :volume 8}]}]
从而跟踪哪个容器可以进入哪个容器。我开始使用reduce,但是如果不使用变异商店来保存剩余的罐头,就无法理解如何做到这一点。有什么想法吗?
更新
通过填充,我的意思是在第一个容器中装入尽可能多的罐子,直到它装满或接近(罐子的体积总和不超过容器的体积),然后开始填充第二个容器,直到它为止完全或接近,等等。
答案 0 :(得分:0)
我们不需要为剩余的罐子使用变异商店,因为reduce将序列作为参数,并减少累积函数,在序列的每个元素上调用它。
在下文中,我将容器分成填充/未填充,并将每个罐插入容器中,它将是最紧密的(这有助于优化包装)。
user> (def container [{:no 1 :volume 10} {:no 2 :volume 20}])
#'user/container
user> (def cans [{:no 1 :volume 2} {:no 2 :volume 8} {:no 1 :volume 5} {:no 2 :volume 8}])
#'user/cans
user>
(def containerized
(let [containers (map #(assoc % :capacity (:volume %) :cans []) container)
insert (fn [container can]
(-> container
(update-in [:capacity] - (:volume can))
(update-in [:cans] conj can)))
accumulator (fn [[filled filling] can]
(let [[non-fits [picked & unpicked]]
(split-with #(< (:capacity %)
(:volume can))
(sort-by :capacity filling))
picked (insert picked can)
still-filling (concat non-fits unpicked)]
(if (<= (:capacity picked) 0)
[(conj filled picked) still-filling]
[filled (conj still-filling picked)])))
filled (reduce accumulator
[[] containers]
cans)]
(map #(dissoc % :capacity) (apply concat filled))))
#'user/containerized
user> (clojure.pprint/pprint containerized)
({:cans [{:volume 2, :no 1} {:volume 8, :no 2}], :volume 10, :no 1}
{:cans [{:volume 5, :no 1} {:volume 8, :no 2}], :volume 20, :no 2})
nil