如何递归地找到整数列表中的最大元素?

2024-02-28

我正在尝试编写一个函数,它将递归地查找整数列表中的最大元素。我知道如何在 Java 中执行此操作,但无法理解如何在 Scala 中执行此操作。

这是我到目前为止所拥有的,但没有递归:

  def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new java.util.NoSuchElementException();
    else xs.max;
  }

我们怎样才能用Scala语义递归地找到它呢?


这是我能想到的 max 的最小递归实现:

def max(xs: List[Int]): Option[Int] = xs match {
  case Nil => None
  case List(x: Int) => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
} 

它的工作原理是比较列表中的前两个元素,丢弃较小的元素(或第一个,如果两者相等),然后在剩余列表上调用自身。最终,这会将列表减少到一个必须是最大的元素。

我返回一个选项来处理给出空列表而不引发异常的情况 - 这迫使调用代码识别这种可能性并处理它(由调用者决定,如果they想要抛出异常)。

如果你想让它更通用,应该这样写:

def max[A <% Ordered[A]](xs: List[A]): Option[A] = xs match {
  case Nil => None
  case x :: Nil => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
}

它将适用于任何扩展的类型Ordered特征或存在隐式转换A to Ordered[A]在适用范围。所以默认情况下它适用于Int, BigInt, Char, String等等,因为 scala.Predef 为它们定义了转换。

我们可以变得更加通用,如下所示:

def max[A <% Ordered[A]](xs: Seq[A]): Option[A] = xs match {
  case s if s.isEmpty || !s.hasDefiniteSize => None
  case s if s.size == 1 => Some(s(0))
  case s if s(0) <= s(1) => max(s drop 1)
  case s => max((s drop 1).updated(0, s(0)))
}

它不仅适用于列表,还适用于向量和任何其他扩展的集合Seq特征。请注意,我必须添加一个检查来查看序列是否实际上具有确定的大小 - 它可能是一个无限流,因此如果可能是这种情况,我们会后退。如果你确定你的流有一个确定的大小,你总是可以在调用这个函数之前强制它 - 无论如何它都会在整个流中工作。请参阅最后的注释了解为什么我really不想回来None不过,对于无限流。我在这里这样做纯粹是为了简单起见。

但这不适用于集合和地图。该怎么办?下一个常见的超类型是Iterable,但这不支持updated或任何同等的东西。我们构造的任何东西对于实际类型来说都可能表现得很差。所以我的干净的无辅助函数递归失败了。我们could更改为使用辅助函数,但其​​他答案中有很多示例,我将坚持使用单简单函数方法。所以此时我们可以切换到reduceLeft(当我们这样做时,让我们选择“Traversable”并满足all集合):

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = {
  if (xs.hasDefiniteSize) 
    xs reduceLeftOption({(b, a) => if (a >= b) a else b}) 
  else None
}

但如果你不考虑reduceLeft递归,我们可以这样做:

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = xs match {
  case i if i.isEmpty => None
  case i if i.size == 1 => Some(i.head)
  case i if (i collect { case x if x > i.head => x }).isEmpty => Some(i.head)
  case _ => max(xs collect { case x if x > xs.head => x })
}

它使用collect组合器以避免一些笨拙的方法来避免新迭代器的出现xs.head and xs drop 2.

其中任何一个都可以安全地处理几乎任何有订单的集合。例子:

scala>  max(Map(1 -> "two", 3 -> "Nine", 8 -> "carrot"))
res1: Option[(Int, String)] = Some((8,carrot))

scala> max("Supercalifragilisticexpialidocious")
res2: Option[Char] = Some(x)

我通常不会举其他例子,因为它需要更多的 Scala 专业知识。

另外,请记住基本的Traversable特质提供了max方法,所以这只是为了练习;)

注意:我希望我的所有示例都表明,仔细选择 case 表达式的顺序可以使每个单独的 case 表达式尽可能简单。

更重要的注意事项:哦,还有,虽然我回来时感到非常舒服None对于输入Nil,在实践中我强烈倾向于抛出异常hasDefiniteSize == false。首先,有限流可以具有完全取决于评估顺序的确定或不确定的大小,并且该函数将有效地随机返回Option在这些情况下 - 可能需要很长时间才能找到。其次,我希望人们能够区分是否通过了Nil并通过了真正的风险输入(即无限流)。我才回来Option在这些演示中,使代码尽可能简单。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何递归地找到整数列表中的最大元素? 的相关文章

随机推荐