【问题标题】:Is there a more idiomatic way to get N random elements of a collection in Clojure?有没有更惯用的方法来获取 Clojure 中集合的 N 个随机元素?
【发布时间】:2013-08-21 09:58:49
【问题描述】:

我目前正在这样做:(repeatedly n #(rand-nth (seq coll))),但我怀疑可能有更惯用的方式,原因有两个:

  • 我发现使用短匿名函数通常有更简洁和更具表现力的替代方法,例如partial
  • repeatedly 的文档字符串说“可能有副作用”,暗示它不打算用于产生值

我想我可以找到一种使用 reduce 的方法,但这似乎很棘手且效率较低,因为它必须处理整个集合,因为 reduce 并不懒惰。

【问题讨论】:

  • 如果你想要可能的重复,你的代码是可以的,except你可以考虑使用向量而不是序列,这样你就不会为每个@987654326 产生线性时间查找@.
  • 请注意,关于“可能有副作用”的部分适用于此。对rand-nth 的调用具有改变随机数生成器状态的副作用。如果没有副作用,那么每次调用都会返回相同的结果,因此您可能不需要多次调用它。
  • “可能有副作用”并不意味着它不应该被用来产生价值。它只是暗示如果您的函数是纯函数(没有副作用),那么您将获得 n 倍相同的值。所以repeatedly 仅在函数不是纯函数时才有用 - 并且 rand fns 不是纯函数,因此您的代码一切正常。
  • 您的代码有问题:您在 coll 上调用 seq,从而将 coll 按顺序转换。这意味着nth 现在将需要线性时间(count 也有可能)。在一个大集合上,它会大大降低性能。最好转换为向量(使用vec)并将结果向量提升出闭包(以免每次都转换它)。一种提升方法是使用旧的let。另一种是使用partial(repeatedly n (partial rand-nth (vec coll)))
  • @cgrand 有见地,谢谢!!

标签: clojure idioms


【解决方案1】:

一个简单但不适用于大型收藏的解决方案可能是:

(take n (shuffle coll))

具有不重复元素的“优势”。你也可以实现一个惰性洗牌,但它会涉及更多的代码。

【讨论】:

    【解决方案2】:

    我知道这并不完全符合您的要求 - 但如果您正在进行大量抽样和统计工作,您可能会对 Incanter ([incanter "1.5.2"]) 感兴趣。 Incanter 提供了函数sample,它提供了样本大小和替换的选项。

    (require '[incanter.stats :refer [sample]]))
    
    (sample [1 2 3 4 5 6 7] :size 5 :replacement false) 
    ; => (1 5 6 2 7)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2013-09-11
      • 2014-11-18
      • 2021-02-22
      • 2013-08-06
      • 1970-01-01
      相关资源
      最近更新 更多