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


希望这能有所帮助。祝您玩得开心!您正走在正确的道路上。

...