我正在尝试编写一个函数,它将以递归方式查找整数列表中的最大元素。我知道如何在Java中执行此操作,但无法理解如何在Scala中执行此操作。
这是我到目前为止所做的,但没有递归:
def max(xs: List[Int]): Int = {
if (xs.isEmpty) throw new java.util.NoSuchElementException();
else xs.max;
}
我们如何使用Scala语义递归地找到它。
答案 0 :(得分:36)
这是我曾经能想到的最小的递归实现:
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 )
}
它通过比较列表中的前两个元素,丢弃较小的(或第一个,如果两者都相等),然后在剩余列表上调用自身。最终,这会将列表缩减为一个必须最大的元素。
我返回一个选项来处理给出一个空列表而不抛出异常的情况 - 这会强制调用代码识别可能性并处理它(如果他们则由调用者处理)想要抛出异常)。
如果你想要它更通用,它应该写成这样:
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
隐式转换为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
特征的集合。请注意,我必须添加一个检查以查看序列是否确实具有确定的大小 - 它可能是一个无限的流,所以如果可能是这种情况,我们会退回。如果您确定您的流将具有确定的大小,您可以在调用此函数之前始终强制它 - 无论如何它将在整个流中工作。最后请参阅注释,了解为什么我确实不希望返回None
无限期流。我这里只是为了简单起见。
但这不适用于集合和地图。该怎么办?下一个常见的超类型是Iterable
,但这不支持updated
或任何等效的。我们构建的任何东西对于实际类型都可能表现不佳。所以我干净的无辅助函数递归会崩溃。我们可以改为使用辅助函数,但是在其他答案中有很多例子,我将坚持使用一个简单的函数方法。所以在这一点上,我们可以切换到reduceLeft
(当我们在它的时候,让我们去'Traversable'并迎合所有集合):
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
和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
方法,所以这只是为了练习;)
注意:我希望我的所有示例都显示如何仔细选择案例表达式的序列,使每个案例表达式尽可能简单。
更重要的注意事项:哦,同样,虽然我非常乐意回复None
输入Nil
,但实际上我非常倾向于抛出一个hasDefiniteSize == false
的例外情况。首先,有限流可以具有明确或非确定的大小,纯粹依赖于评估序列,并且在这些情况下该函数将有效地随机返回Option
- 这可能需要很长时间才能跟踪。其次,我希望人们能够区分已经通过Nil
和通过真正的风险输入(即无限流)。我只在这些演示中返回Option
,以使代码尽可能简单。
答案 1 :(得分:16)
最简单的方法是使用TraversableOnce
特征的最大函数,如下所示,
val list = (1 to 10).toList
list.max
要防止空虚,你可以做这样的事情,
if(list.empty) None else Some(list.max)
上面会给你一个Option[Int]
我的第二种方法是使用foldLeft
(list foldLeft None)((o, i) => o.fold(Some(i))(j => Some(Math.max(i, j))))
或者如果你知道在空列表的情况下要返回的默认值,这将变得更加简单。
val default = 0
(list foldLeft default)(Math.max)
无论如何,因为你的要求是以递归方式进行,我建议关注,
def recur(list:List[Int], i:Option[Int] = None):Option[Int] = list match {
case Nil => i
case x :: xs => recur(xs, i.fold(Some(x))(j => Some(Math.max(j, x))))
}
或默认情况下,
val default = 0
def recur(list:List[Int], i:Int = default):Int = list match {
case Nil => i
case x :: xs => recur(xs, i.fold(x)(j => Math.max(j, x)))
}
请注意,这是tail recursive
。因此堆栈也被保存。
答案 2 :(得分:13)
如果您想要解决此问题的功能方法,请使用reduceLeft
:
def max(xs: List[Int]) = {
if (xs.isEmpty) throw new NoSuchElementException
xs.reduceLeft((x, y) => if (x > y) x else y)
}
这个特定于整数列表的函数,如果你需要更通用的方法,那么使用Ordering
类型类:
def max[A](xs: List[A])(implicit cmp: Ordering[A]): A = {
if (xs.isEmpty) throw new NoSuchElementException
xs.reduceLeft((x, y) => if (cmp.gteq(x, y)) x else y)
}
reduceLeft
是一个高阶函数,它接受(A, A) => A
类型的函数,在这种情况下需要两个整数,比较它们并返回更大的一个。
答案 3 :(得分:8)
您可以使用类似
的模式匹配def max(xs: List[Int]): Int = xs match {
case Nil => throw new NoSuchElementException("The list is empty")
case x :: Nil => x
case x :: tail => x.max(max(tail)) //x.max is Integer's class method
}
答案 4 :(得分:6)
Scala是一种功能性语言,鼓励人们以递归方式思考。我的解决方案如下。我根据你给定的方法重复它。
def max(xs: List[Int]): Int = {
if(xs.isEmpty == true) 0
else{
val maxVal= max(xs.tail)
if(maxVal >= xs.head) maxVal
else xs.head
}
}
通过建议更新了我的尾递归解决方案。
def max(xs: List[Int]): Int = {
def _max(xs: List[Int], maxNum: Int): Int = {
if (xs.isEmpty) maxNum
else {
val max = {
if (maxNum >= xs.head) maxNum
else xs.head
}
_max(xs.tail, max)
}
}
_max(xs.tail, xs.head)
}
答案 5 :(得分:1)
我只使用head()和tail()
def max(xs: List[Int]): Int = {
if (xs.isEmpty) throw new NoSuchElementException
else maxRecursive(xs.tail,xs.head)
}
def maxRecursive(xs: List[Int], largest: Int): Int = {
if (!xs.isEmpty ){
if (xs.head > largest) maxRecursive(xs.tail, xs.head)
else maxRecursive(xs.tail, largest)
}else{
largest
}
}
这是测试:
test("max of a few numbers") {
assert(max(List(3, 7, 2, 1, 10)) === 10)
assert(max(List(3, -7, 2, -1, -10)) === 3)
assert(max(List(-3, -7, -2, -5, -10)) === -2)
}
答案 6 :(得分:0)
折叠可以提供帮助:
if(xs.isEmpty)
throw new NoSuchElementException
else
(Int.MinValue /: xs)((max, value) => math.max(max, value))
列表和模式匹配(已更新,感谢@ x3ro)
def max(xs:List[Int], defaultValue: =>Int):Int = {
@tailrec
def max0(xs:List[Int], maxSoFar:Int):Int = xs match {
case Nil => maxSoFar
case head::tail => max0(tail, math.max(maxSoFar, head))
}
if(xs.isEmpty)
defaultValue
else
max0(xs, Int.MinValue)
}
(此解决方案不会每次都创建Option
实例。它也是尾递归的,并且与命令式解决方案一样快。)
答案 7 :(得分:0)
看起来你刚刚开始使用scala,所以我尝试给你最简单的答案,如何递归:
def max(xs: List[Int]): Int = {
def maxrec(currentMax : Int, l: List[Int]): Int = l match {
case Nil => currentMax
case head::tail => maxrec(head.max(currentMax), tail) //get max of head and curretn max
}
maxrec(xs.head, xs)
}
此方法定义了一个自己的内部方法(maxrec
)来处理递归。它会失败(例外)你给它一个空列表(空列表上没有最大值)
答案 8 :(得分:0)
这是我的代码(我是函数式编程的新手),我假设在这个问题下出现的人都会像我一样。最好的答案,虽然很棒,对于新手来说有点太多了!所以,这是我简单的答案。请注意,我被要求(作为课程的一部分)仅使用头部和尾部进行此操作。
/**
* This method returns the largest element in a list of integers. If the
* list `xs` is empty it throws a `java.util.NoSuchElementException`.
*
* @param xs A list of natural numbers
* @return The largest element in `xs`
* @throws java.util.NoSuchElementException if `xs` is an empty list
*/
@throws(classOf[java.util.NoSuchElementException])
def max(xs: List[Int]): Int = find_max(xs.head, xs.tail)
def find_max(max: Int, xs: List[Int]): Int = if (xs.isEmpty) max else if (max >= xs.head) find_max(max, xs.tail) else find_max(xs.head, xs.tail)
一些测试:
test("max of a few numbers") {
assert(max(List(3, 7, 2)) === 7)
intercept[NoSuchElementException] {
max(List())
}
assert(max(List(31,2,3,-31,1,2,-1,0,24,1,21,22)) === 31)
assert(max(List(2,31,3,-31,1,2,-1,0,24,1,21,22)) === 31)
assert(max(List(2,3,-31,1,2,-1,0,24,1,21,22,31)) === 31)
assert(max(List(Int.MaxValue,2,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,22)) === Int.MaxValue)
}
答案 9 :(得分:0)
list.sortWith(_&gt; )。head&amp; list.sortWith(&gt; _)。reverse.head表示最大和最小的数字
答案 10 :(得分:0)
如果你需要使用isEmpty在head列表中编写递归max函数,head和tail并为空列表抛出异常:
def max(xs: List[Int]): Int =
if (xs.isEmpty) throw new NoSuchElementException("max of empty list")
else if (xs.tail.isEmpty) xs.head
else if (xs.head > xs.tail.head) max(xs.head :: xs.tail.tail)
else max(xs.tail)
如果您在列表中使用max函数,则它很简单(您不需要编写自己的递归函数):
val maxInt = List(1, 2, 3, 4).max
答案 11 :(得分:0)
def max(xs: List[Int]): Int = {
def _max(xs: List[Int], maxAcc:Int): Int = {
if ( xs.isEmpty )
maxAcc
else
_max( xs.tail, Math.max( maxAcc, xs.head ) ) // tail call recursive
}
if ( xs.isEmpty )
throw new NoSuchElementException()
else
_max( xs, Int.MinValue );
}
答案 12 :(得分:0)
具有尾递归功能
@tailrec
def findMax(x: List[Int]):Int = x match {
case a :: Nil => a
case a :: b :: c => findMax( (if (a > b) a else b) ::c)
}
答案 13 :(得分:0)
具有模式匹配功能以查找最大值,如果为空则返回零
def findMax(list: List[Int]) = {
def max(list: List[Int], n: Int) : Int = list match {
case h :: t => max(t, if(h > n) h else n)
case _ => n
}
max(list,0)
}
答案 14 :(得分:0)
我想这是为progfun示例
这是我能想到的最简单的递归解决方案
def max(xs: List[Int]): Int = {
if (xs.isEmpty) throw new NoSuchElementException("The list is empty")
val tail = xs.tail
if (!tail.isEmpty) maxOfTwo(xs.head, max(xs.tail))
else xs.head
}
def maxOfTwo(x: Int, y: Int): Int = {
if (x >= y) x
else y
}
答案 15 :(得分:-2)
def max(xs: List[Int]): Int = xs match {
case Nil => throw new NoSuchElementException("empty list!")
case x :: Nil => x
case x :: tail => if (x > max(tail)) x else max(tail)
}