给出Int类型和Int类型的变量X. Scala 功能方式中最好的方法是仅保留List中的那些值(从列表的开头开始),使得列表值的总和小于等于变量。
答案 0 :(得分:5)
这非常接近单线:
def takeWhileLessThan(x: Int)(l: List[Int]): List[Int] =
l.scan(0)(_ + _).tail.zip(l).takeWhile(_._1 <= x).map(_._2)
让我们把它分成小块。
首先,您使用scan
创建累积总和列表。以下是一个小例子的工作原理:
scala> List(1, 2, 3, 4).scan(0)(_ + _)
res0: List[Int] = List(0, 1, 3, 6, 10)
请注意,结果包含初始值,这就是我们在实现中使用tail
的原因。
scala> List(1, 2, 3, 4).scan(0)(_ + _).tail
res1: List[Int] = List(1, 3, 6, 10)
现在我们将整个内容压缩到原始列表中。再举一个例子,这看起来如下:
scala> List(1, 2, 3, 4).scan(0)(_ + _).tail.zip(List(1, 2, 3, 4))
res2: List[(Int, Int)] = List((1,1), (3,2), (6,3), (10,4))
现在,我们可以使用takeWhile
在累积总和大于目标之前从此列表中获取尽可能多的值。假设我们的例子中我们的目标是5:
scala> res2.takeWhile(_._1 <= 5)
res3: List[(Int, Int)] = List((1,1), (3,2))
这几乎是我们想要的 - 我们只需要摆脱累积总和:
scala> res2.takeWhile(_._1 <= 5).map(_._2)
res4: List[Int] = List(1, 2)
我们已经完成了。值得注意的是,这不是非常有效,因为它计算整个列表的累积总和等等。实现可以通过各种方式进行优化,但实际上它可能是在Scala中执行此操作的最简单的纯函数方式(在大多数情况下,性能不会成为问题,无论如何)。
答案 1 :(得分:1)
除了Travis的回答(并且为了完整性),您始终可以将这些类型的操作实现为foldLeft
:
def takeWhileLessThanOrEqualTo(maxSum: Int)(list: Seq[Int]): Seq[Int] = {
// Tuple3: the sum of elements so far; the accumulated list; have we went over x, or in other words are we finished yet
val startingState = (0, Seq.empty[Int], false)
val (_, accumulatedNumbers, _) = list.foldLeft(startingState) {
case ((sum, accumulator, finished), nextNumber) =>
if(!finished) {
if (sum + nextNumber > maxSum) (sum, accumulator, true) // We are over the sum limit, finish
else (sum + nextNumber, accumulator :+ nextNumber, false) // We are still under the limit, add it to the list and sum
} else (sum, accumulator, finished) // We are in a finished state, just keep iterating over the list
}
accumulatedNumbers
}
这只迭代列表一次,所以它应该更有效,但更复杂,需要一些阅读代码才能理解。
答案 2 :(得分:1)
我会选择这样的东西,它更实用,应该更有效率。
def takeSumLessThan(x:Int,l:List[Int]): List[Int] = (x,l) match {
case (_ , List()) => List()
case (x, _) if x<= 0 => List()
case (x, lh :: lt) => lh :: takeSumLessThan(x-lh,lt)
}
编辑1:添加尾递归并隐含更短的呼叫符号
import scala.annotation.tailrec
implicit class MyList(l:List[Int]) {
def takeSumLessThan(x:Int) = {
@tailrec
def f(x:Int,l:List[Int],acc:List[Int]) : List[Int] = (x,l) match {
case (_,List()) => acc
case (x, _ ) if x <= 0 => acc
case (x, lh :: lt ) => f(x-lh,lt,acc ++ List(lh))
}
f(x,l,Nil)
}
}
现在您可以像
一样使用它List(1,2,3,4,5,6,7,8).takeSumLessThan(10)