我想编写简洁的代码来映射列表,在我去的时候累积一个值并在输出列表中使用该值。
使用递归函数和模式匹配这很简单(见下文)。但是我想知道是否有办法使用组合函数编程系列,如map和fold等。显然map和fold是没有用的,除非你使用在调用之外定义的可变变量并在体内修改它。 / p>
也许我可以通过State Monad做到这一点,但是想知道是否有一种方法可以解决这个问题,而且我使用的是Scala标准库。
// accumulate(List(10, 20, 20, 30, 20))
// => List(10, 30, 50, 80, 100,)
def accumulate(weights : List[Int], sum : Int = 0, acc: List[Int] = List.empty) : List[Int] = {
weights match {
case hd :: tl =>
val total = hd + sum
accumulate(tl, total, total :: acc)
case Nil =>
acc.reverse
}
}
答案 0 :(得分:4)
您也可以使用foldLeft
:
def accumulate(seq: Seq[Int]) =
seq.foldLeft(Vector.empty[Int]) { (result, e) =>
result :+ result.lastOption.getOrElse(0) + e
}
accumulate(List(10, 20, 20, 30, 20))
// => List(10, 30, 50, 80, 100,)
答案 1 :(得分:3)
这可以通过扫描完成:
val result = list.scanLeft(0){case (acc, item) => acc+item}
扫描将初始值0包含在输出中,因此您必须删除它:
result.drop(1)
答案 2 :(得分:2)
正如@Nyavro's answer中所指出的,您正在寻找的操作(列表前缀的总和)称为prefix-sum,并且它对任何二进制操作的泛化称为scan
并且是included in the Scala standard library:
val l = List(10, 20, 20, 30, 20)
l.scan(0) { _ + _ }
//=> List(0, 10, 30, 50, 80, 100)
l.scan(0)(_ + _).drop(1)
//=> List(10, 30, 50, 80, 100)
这已经得到了解答,但我想在你的问题中解决一个误解:
显然map和fold是没有用的,除非你使用在调用之外定义的可变变量并在体内修改它。
事实并非如此。 fold
是一种通用迭代方法。你可以通过遍历集合来完成 Everything ,你可以使用fold
。如果fold
类中的List
是唯一的方法,您仍然可以执行现在可以执行的所有操作。以下是使用fold
解决问题的方法:
l.foldLeft(List(0)) { (list, el) ⇒ list.head + el :: list }.reverse.drop(1)
scan
的一般实现:
def scan[A](l: List[A])(z: A)(op: (A, A) ⇒ A) =
l.
drop(1).
foldLeft(List(l.head)) { (list, el) ⇒ op(list.head, el) :: list }.
reverse
以这种方式思考:集合可以是空的也可以不是。 fold
有两个参数,一个告诉它在列表为空时要做什么,另一个告诉它在列表不为空时该怎么做。这是唯一的两个案例,因此每个可能的案件都得到处理。因此,fold
可以做任何事情! (更准确地说,在Scala中,foldLeft
和foldRight
可以执行任何操作,而fold
仅限于关联操作。)
或者不同的观点:集合是指令流,可以是EMPTY
指令,也可以是ELEMENT(value)
指令。 foldLeft
/ foldRight
是该指令集的骨架解释器,作为程序员,您可以提供解释这两个指令的实现,即{{{{ 1}} / foldLeft
是对这些说明的解释。
请记住:当foldRight
/ foldLeft
将集合缩减为单个值时,该值可能是任意复杂的,包括作为集合本身!