【问题标题】:Find min and max elements of array查找数组的最小和最大元素
【发布时间】:2013-12-15 14:49:34
【问题描述】:

我想使用 for comprehension 来查找数组的最小和最大元素。是否可以通过一次迭代数组来找到最小元素和最大元素?

我正在寻找不使用 scala 提供的 array.min 或 max 的解决方案。

【问题讨论】:

  • 我会从这个链接开始,一些 cmets 可能会很有帮助link
  • 嘿我不想使用 min 函数...我正在寻找不使用 min 函数的解决方案
  • 我认为我符合您的所有标准,@Rajeev,尤其是在处理大量列表时。请参阅下面的“Scala 惯用”解决方案我的答案:stackoverflow.com/a/55959734/501113

标签: scala


【解决方案1】:

您可以使用reduceLeft 函数获取 Array[Int] 的最小值和最大值。

scala> val a = Array(20, 12, 6, 15, 2, 9)

a: Array[Int] = Array(20, 12, 6, 15, 2, 9)

scala> a.reduceLeft(_ min _)

res: Int = 2

scala> a.reduceLeft(_ max _)

分辨率:Int = 20

有关reduceLeft方法的更多信息和示例,请参阅此链接:http://alvinalexander.com/scala/scala-reduceleft-examples

【讨论】:

  • 这似乎是一种简洁/惯用的方法
  • 这个答案是INCORRECT!如果您仔细阅读原始问题,发帖人会问 IN A SINGLE PASS 如何同时收集最小值和最大值。这个答案展示了如何在两次通行证中做到这一点,这正是原始海报试图避免的。
  • 如果处理大量列表,原始发帖人请求的单次通过部分很重要。此外,此解决方案不是通用的。请参阅下面的“Scala 惯用”解决方案我的答案:stackoverflow.com/a/55959734/501113
【解决方案2】:

这是一个简洁易读的解决方案,可以避免丑陋的if 语句:

def minMax(a: Array[Int]) : (Int, Int) = {
  if (a.isEmpty) throw new java.lang.UnsupportedOperationException("array is empty")
  a.foldLeft((a(0), a(0)))
  { case ((min, max), e) => (math.min(min, e), math.max(max, e))}
}

解释:foldLeft 是 Scala 中许多集合的标准方法。它允许将一个累加器传递给一个回调函数,该函数将为数组的每个元素调用。

查看scaladoc了解更多详情

【讨论】:

  • 你能否在你的解决方案中添加一些关于 foldLeft 和 e 工作原理的描述
  • 可能是 IAE? UnsupportedOperationException 用于完全不同的事情(当整个方法不被支持作为一种类型时,例如不可变集合的删除方法)。这样,您可以使用require(a.nonEmpty, "array is empty") 而不是 if。
  • @om-nom-nom 我选择 UOE 是为了与当前 API 保持一致(方法 min 在数组为空时抛出 UOE)。
  • 等一下,if,一个非常清晰的词,你不需要任何其他东西就可以理解英文字典是“丑陋的”,但是 foldLeft 带有闭包、模式匹配和函数调用数学库更好?我喜欢函数式范式,但我愿意喝这么多的冷却剂;-)
  • 此解决方案将执行无关比较。如果处理一个巨大的列表,这很重要。当要替换最小值时,没有理由执行最大值比较。此外,此解决方案不是通用的。请参阅下面的“Scala 惯用”解决方案我的答案:stackoverflow.com/a/55959734/501113
【解决方案3】:
def findMinAndMax(array: Array[Int]) = { // a non-empty array

    val initial = (array.head, array.head)  // a tuple representing min-max


    // foldLeft takes an initial value of type of result, in this case a tuple
    // foldLeft also takes a function of 2 parameters.
    // the 'left' parameter is an accumulator (foldLeft -> accum is left)
    // the other parameter is a value from the collection.

    // the function2 should return a value which replaces accumulator (in next iteration)
    // when the next value from collection will be picked.

    // so on till all values are iterated, in the end accum is returned.

    array.foldLeft(initial) { ((min, max), x) => 
          if (x < min) (x, max) 
          else if (x > max) (min, x) 
          else acc 
    }
}

【讨论】:

  • 你能解释一下你的代码吗?我是scala的首发。感觉很难理解 foldLeft 是如何工作的
  • 我同意使用 acc._1 的实现会使阅读变得混乱。至少这个解决方案不会执行无关的比较,这在处理一个巨大的列表时很重要。此外,此解决方案不是通用的。请参阅下面的“Scala 惯用”解决方案我的答案:stackoverflow.com/a/55959734/501113
【解决方案4】:

从其他答案开始 - 一个更通用的解决方案是可能的,它适用于其他集合以及Array,以及其他内容以及Int

def minmax[B >: A, A](xs: Iterable[A])(implicit cmp: Ordering[B]): (A, A) = {
  if (xs.isEmpty) throw new UnsupportedOperationException("empty.minmax")

  val initial = (xs.head, xs.head)

  xs.foldLeft(initial) { case ((min, max), x) => 
    (if (cmp.lt(x, min)) x else min, if (cmp.gt(x, max)) x else max) }
}                                               

例如:

minmax(List(4, 3, 1, 2, 5))              //> res0: (Int, Int) = (1,5)

minmax(Vector('Z', 'K', 'B', 'A'))       //> res1: (Char, Char) = (A,Z)

minmax(Array(3.0, 2.0, 1.0))             //> res2: (Double, Double) = (1.0,3.0)

(也可以使用cmp.min()cmp.max() 更简洁地编写此代码,但前提是您删除了B &gt;: A 类型绑定,这会降低函数的通用性)。

【讨论】:

  • 此解决方案将执行无关比较。如果处理一个巨大的列表,这很重要。当要替换最小值时,没有理由执行最大值比较。有关改进的“Scala 惯用”解决方案,请参阅下面的答案:stackoverflow.com/a/55959734/501113
【解决方案5】:

考虑一下(对于非空可排序数组),

val ys = xs.sorted
val (min,max) = (ys.head, ys.last)

【讨论】:

  • 对于大型阵列效率低下。所需要的只是最小值和最大值,没有必要(因此浪费)对所有其他元素进行排序。
  • 首先,我们需要排序,然后我们需要选择head和last。使计算费用增加。不适合大数组和列表。
【解决方案6】:
val xs: Array[Int] = ???

var min: Int = Int.MaxValue
var max: Int = Int.MinValue

for (x <- xs) {
  if (x < min) min = x
  if (x > max) max = x
}

【讨论】:

  • 这个解决方案根本不是惯用的 Scala。此解决方案还将执行无关的比较。如果处理一个巨大的列表,这很重要。当要替换最小值时,没有理由执行最大值比较。此外,此解决方案不是通用的。请参阅下面的“Scala 惯用”解决方案我的答案:stackoverflow.com/a/55959734/501113
  • 看起来像'C'翻译成scala。 var的?
【解决方案7】:

我在这个聚会上迟到了,但我是 Scala 的新手,我想无论如何我都会做出贡献。这是使用尾递归的解决方案:

@tailrec
def max(list: List[Int], currentMax: Int = Int.MinValue): Int = {
    if(list.isEmpty) currentMax
    else if ( list.head > currentMax) max(list.tail, list.head)
    else max(list.tail,currentMax)
}

【讨论】:

  • 您似乎没有阅读原始发帖人的请求。您的解决方案的最小部分在哪里?此外,此解决方案不是通用的。请参阅下面的“Scala 惯用”解决方案我的答案:stackoverflow.com/a/55959734/501113
  • 集合中的 foldLeft 有效地提供了使用“尾递归”实现的解决方案。
【解决方案8】:

在我对这个问题的所有回答中,DNA 的解决方案是我能找到的最接近“Scala 惯用语”的解决方案。但是,它可以通过...稍微改进:

  1. 根据需要执行尽可能少的比较(对于非常大的集合很重要)
  2. 仅使用 Ordering.lt 方法提供理想的排序一致性
  3. 避免抛出异常
  4. 让代码对于刚接触和学习 Scala 的人来说更具可读性

cmets 应有助于澄清更改。

def minAndMax[B>: A, A](iterable: Iterable[A])(implicit ordering: Ordering[B]): Option[(A, A)] =
  if (iterable.nonEmpty)
    Some(
      iterable.foldLeft((iterable.head, iterable.head)) {
        case (minAndMaxTuple, element) =>
          val (min, max) =
            minAndMaxTuple //decode reference to tuple
          if (ordering.lt(element, min))
            (element, max) //if replacing min, it isn't possible max will change so no need for the max comparison
          else
            if (ordering.lt(max, element))
              (min, element)
            else
              minAndMaxTuple //use original reference to avoid instantiating a new tuple
      }
    )
  else
    None

这里的解决方案扩展为在单次通过中返回二维空间的下限和上限,再次使用上述优化:

def minAndMax2d[B >: A, A](iterable: Iterable[(A, A)])(implicit ordering: Ordering[B]): Option[((A, A), (A, A))] =
  if (iterable.nonEmpty)
    Some(
      iterable.foldLeft(((iterable.head._1, iterable.head._1), (iterable.head._2, iterable.head._2))) {
        case ((minAndMaxTupleX, minAndMaxTupleY), (elementX, elementY)) =>
          val ((minX, maxX), (minY, maxY)) =
            (minAndMaxTupleX, minAndMaxTupleY) //decode reference to tuple
          (
              if (ordering.lt(elementX, minX))
                (elementX, maxX) //if replacing minX, it isn't possible maxX will change so no need for the maxX comparison
              else
                if (ordering.lt(maxX, elementX))
                  (minX, elementX)
                else
                  minAndMaxTupleX //use original reference to avoid instantiating a new tuple
            , if (ordering.lt(elementY, minY))
                (elementY, maxY) //if replacing minY, it isn't possible maxY will change so no need for the maxY comparison
              else
                if (ordering.lt(maxY, elementY))
                  (minY, elementY)
                else
                  minAndMaxTupleY //use original reference to avoid instantiating a new tuple
          )
      }
    )
  else
    None

【讨论】:

    【解决方案9】:

    您始终可以编写自己的 foldLeft 函数 - 这将保证一次迭代和已知性能。

    val array = Array(3,4,62,8,9,2,1)
    if(array.isEmpty) throw new IllegalArgumentException // Just so we can safely call array.head
    
    val (minimum, maximum) = array.foldLeft((array.head, array.head)) {  // We start of with the first element as min and max
      case ((min, max), next) =>
        if(next > max) (min, next)
        else if(next < min) (next, max)
        else (min, max)
    }
    
    println(minimum, maximum) //1, 62
    

    【讨论】:

    • 至少这个解决方案不会执行无关的比较。但是,如果最小值和最大值都没有改变(这是遍历次数最多的代码路径),它将重新实例化 (min, max) 元组。如果处理一个巨大的列表,这很重要。此外,此解决方案不是通用的。请参阅下面的“Scala 惯用”解决方案我的答案:stackoverflow.com/a/55959734/501113
    【解决方案10】:
    scala> val v = Vector(1,2)
    scala> v.max
    res0: Int = 2
    scala> v.min
    res1: Int = 2
    

    您可以使用Vectorminmax 方法

    【讨论】:

    • 欢迎来到 StackOverflow。在回答这么老的问题(6 年前问过)时,您必须问自己为什么其他 9 个答案中都没有提到这样一个简单的解决方案。重读问题。它特别要求对集合进行一次传递(您的答案需要 2 次传递)并且它应该没有使用.min.max
    猜你喜欢
    • 1970-01-01
    • 2021-12-25
    • 2014-07-21
    • 2018-10-17
    • 2010-12-12
    • 2017-01-17
    • 2021-12-27
    • 1970-01-01
    相关资源
    最近更新 更多