【问题标题】:How to choose a random element from an array in Scala?如何从Scala中的数组中选择一个随机元素?
【发布时间】:2011-06-30 09:50:35
【问题描述】:

例如,有一个 Scala 数组 val A = Array("please", "help", "me")。如何从这个数组中随机选择一个元素?

【问题讨论】:

    标签: scala scala-2.8


    【解决方案1】:
    import java.util.Random
    // ...
    val rand = new Random(System.currentTimeMillis())
    val random_index = rand.nextInt(A.length)
    val result = A(random_index)
    

    【讨论】:

    • 虽然这里的目的是不安全的伪随机性,但自播 RNG 是不好的做法,除非你真的非常清楚自己在做什么。设置为当前时间特别糟糕,因为它显着缩小了可能种子的窗口。有关更多信息,请参阅本系列:jazzy.id.au/default/2010/09/20/…
    • scala.util.Random 具有随机播放功能,这要简单得多,尤其是在您需要多个随机元素时。请参阅下面的答案。
    • 如果A.length 等于零,这将崩溃。永远不要忘记边缘情况
    • 这种边缘情况是需求中固有的。
    【解决方案2】:
    import scala.util.Random
    
    val A = Array("please", "help", "me")
    Random.shuffle(A.toList).head
    

    【讨论】:

    • 对整个列表进行重新排序并选择头部是否比仅在 O(1) 中选择数组值有效?
    • c2.com/cgi/wiki?PrematureOptimization该问题没有说明任何效率需求,实际上给出了一个很短的Array的例子:)
    • 为什么应该是临时的?它有效,并且不太容易在数组边界中出现任何编码错误。
    • 应该是headOption,因为Seq(这里是Array)可能是空的。
    • A 不为空。它包含三个字符串。
    【解决方案3】:
    import scala.util.Random
    
    val A = List(1, 2, 3, 4, 5, 6)
    A(Random.nextInt(A.size))
    

    【讨论】:

    • 如果A.size 等于零,这将崩溃。永远不要忘记边缘情况
    • 哦,你可以使用A.lift(Random.nextInt(A.size)),它会给你Option[Int]
    • @JonOnstott 也会在 A.size 为 0 时崩溃
    【解决方案4】:

    完全不涉及重新洗牌的更好答案是:

    import scala.util.Random
    
    object sample {
      //gets random element from array
      def arr[T](items:Array[T]):T = {
        items(Random.nextInt(items.length))
      }
    }
    

    这也通用

    【讨论】:

    • 考虑 sample.arr[Int](Array[Int]())
    【解决方案5】:

    我们还可以使用Option monad(使用lift 方法)添加一些安全性

    实际上,在任何集合上使用此方法时, 即使您的集合为空,或者您的随机索引超出边界,您的结果也将始终是一个选项。

    安全驾驶
    def getRandElemO[T](arr: Array[T]): Option[T] =
      if (arr.isEmpty) None
      else arr.lift(util.Random.nextInt(arr.length))
    
    // or the one liner:
    // def getRandElemO[T](arr: Array[T]): Option[T] =
    //   arr.headOption.flatMap(_ => arr.lift(util.Random.nextInt(arr.length)))
    

    【讨论】:

    • 请注意,它在技术上并不透明,因为使用相同参数的两个调用不会给出相同的结果。要实现这一点,您还必须在参数中传递 RNG 实例。
    【解决方案6】:

    如果您想要更惯用的解决方案,请考虑使用 typeclass 模式(scala 中的隐式类)。

    implicit class ListOps[A](list: List[A]) {
      def getRandomElement: Option[A] = list match {
        case Nil => None
        case _ => list.lift(scala.util.Random.nextInt(list.size))
      }
      def randomChoice(n: Int): Option[List[A]] =
        (1 to n).toList.foldLeft(Option(List[A]()))((acc, e) => getRandomElement.flatMap(r => acc.map(a => a :+ r)))
    }
    

    现在,如果隐式类在范围内,您可以:

    val randomElement: Option[String] = List("this", "is", "a", "list").getRandomElement
    

    如果您确定该选项包含某个值,则可以使用get 方法。

    randomElement.get // This will return a String (or a NotSuchElementExeption)
    

    尽管如此,还是建议使用模式匹配或getOrElse

    randomElement match {
      case None => ??? // This is what you do when a None is encounter (e.g. for empty lists)
      case Some(result) => ??? // The variable result contains a string. 
    

    注意randomChoice 方法假定替换元素。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-23
      • 1970-01-01
      • 2017-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多