2024 Clojure状态调查!中分享您的想法。

欢迎!请参阅关于页面了解如何使用本网站的一些详细信息。

0
Clojure

我正在编写一个Slack机器人,该机器人可以接受一群人和他们所在的办公室,然后将他们分成n个随机分组,并确保两个办公室混合。

我有几个功能可以很好地完成这项任务,但我想知道是否有更“Clojure”的方式来做这件事?我仍在努力摆脱面向对象的习惯,所以任何建议都十分欢迎。

我考虑的这种方法是将它视为一副扑克牌,将人员分发到每个结果组中,直到该办公室的人员用完,然后移动到下一个办公室,直到所有办公室的人员都分发完毕,然后再返回结果。

(defn split 
  "split by office then shuffle
   result is sorted by size"
  [coll]
  (->> (sort-by :office coll)
       (partition-by :office)
       (sort-by count)
       (map shuffle)))

(defn conj-in 
  "inserts an item into a collection's child"
  [coll idx itm]
    (assoc coll idx (conj (nth coll idx) itm)))

(defn split-into-groups
  [coll]
  (let [sorted (split coll)]
    (loop [pool    (rest sorted)
           hand    (first sorted)
           result  (into [] (take @ngroups (repeat [])))
           idx     0]
      
      (if (not-empty hand)
        (let [next (first hand)]
          (recur pool
                 (rest hand)
                 (conj-in result idx next)
                 (mod (inc idx) @ngroups)))
        
        (if (not-empty pool)
          (recur (rest pool)
                 (first pool)
                 result
                 idx)
          
          result)))))

1 个答案

+1

编辑

也许使用interleave

> (interleave [1 2 3] [:a :b :c] ["X" "Y"])
(1 :a "X" 2 :b "Y")

所以,比如这样

(defn people->groups
  [people n-groups]
  (let [group-size (quot (count people) n-groups)]
    (->> (group-by :office people)
         (map (comp shuffle second))
         (apply interleave)
         (partition group-size group-size []))))

使用如下所示

> (def ppl (for [i (range 100)] (let [office (rand-nth [:a :b :c])] {:name (str office "-" i) :office office})))
#'ppl
> (rand-nth ppl)
{:name ":a-19", :office :a}
> (people->groups ppl 4)
...

一些想法

  • 有一些边缘情况需要处理,这取决于您认为当人员数量不能均匀分配到组数量时会发生什么。当前实现会怎么做?为什么?确保“正确”的事情发生是一个有趣的练习 - 也许先做测试然后再固定您喜欢的行为。

  • 从一系列地图开始,就像您有的那样,是很好的 - 我会这么做

  • 您的代码暗示 ngroups 是某些非局部可解引用的,函数参数可能更有意义

  • 我发现我几乎总是可以用clojure.core中的东西重写使用 loop/first/rest 的代码。尽可能多地学习clojure.core


希望这有助于。玩得开心!您已经走在正确的轨道上了。

...