You can also do this in a single pass with a fold:
def take(l: List[Int], limit: Int): List[Int] = l.fold((List.empty[Int], 0)) { case ((res, acc), next) => if (acc + next > limit) (res, limit) else (next :: res, next + acc) }
Since standard lists are not lazy, and none of them add up, this will always go through the entire list. One option would be to use cats' iteratorFoldM instead of this implementation to make short circuits after reaching the limit.
You can also write a short trailing fold using tail recursion, something along these lines:
def take(l: List[Int], limit: Int): List[Int] = { @annotation.tailrec def take0(list: List[Int], accList: List[Int], accSum: Int) : List[Int] = list match { case h :: t if accSum + h < limit => take0(t, h :: accList, h + accSum) case _ => accList } take0(l, Nil, 0).reverse }
Note that this second solution may be faster, but also less elegant, as it takes extra effort to prove that the implementation is complete, something obvious when using addition.
source share