Scala Iteratively build lists

时间:2015-08-14 22:49:35

标签: scala functional-programming scala-collections

As a Scala beginner I am still struggling working with immutable lists. All I am trying to do append elements to my list. Here's an example of what I am trying to do.

val list = Seq()::Nil
val listOfInts = List(1,2,3)
listOfInts.foreach {case x=>
 list::List(x)
}

expecting that I would end up with a list of lists: List(List(1),List(2),List(3))

Coming from java I am used to just using list.add(new ArrayList(i)) to get the same result. Am I way off here?

7 个答案:

答案 0 :(得分:1)

由于List 不可变,您无法修改List

要从列表中构建1个项目列表的列表,您可以在列表上mapforEachmap之间的区别在于forEach不返回任何内容,即Unit,而map从某些函数的返回返回List。

scala> def makeSingleList(j:Int):List[Int] = List(j)
makeSingleList: (j: Int)List[Int]

scala> listOfInts.map(makeSingleList)
res1: List[List[Int]] = List(List(1), List(2), List(3))

答案 1 :(得分:1)

文档页面上有教程。

ListBuffer有一个模糊,如果你这样摆动。

否则,

scala> var xs = List.empty[List[Int]]
xs: List[List[Int]] = List()

scala> (1 to 10) foreach (i => xs = xs :+ List(i))

scala> xs
res9: List[List[Int]] = List(List(1), List(2), List(3), List(4), List(5), List(6), List(7), List(8), List(9), List(10))

您可以选择使用像ListBuffer这样的可变构建器或本地var,并返回您构建的集合。

在功能性世界中,您经常通过预先构建然后反转来构建:

scala> var xs = List.empty[List[Int]]
xs: List[List[Int]] = List()

scala> (1 to 10) foreach (i => xs = List(i) :: xs)

scala> xs.reverse
res11: List[List[Int]] = List(List(1), List(2), List(3), List(4), List(5), List(6), List(7), List(8), List(9), List(10))

答案 2 :(得分:1)

下面是使用添加的print语句从Scala REPL进行复制和粘贴以查看发生的情况:

scala>     val list = Seq()::Nil
list: List[Seq[Nothing]] = List(List())

scala>     val listOfInts = List(1,2,3)
listOfInts: List[Int] = List(1, 2, 3)

scala>     listOfInts.foreach { case x=>
 |       println(list::List(x))
 |     }
List(List(List()), 1)
List(List(List()), 2)
List(List(List()), 3)

在foreach循环的第一次迭代中,您实际上是在获取listOfInts的第一个元素(即1),将其放入新列表(List(1)),然后添加新元素列表(列表(List()))到List(1)的开头。这就是它打印出List(List(List()),1)。

的原因

由于您的list和listOfInts都是不可变的,因此您无法更改它们。您所能做的就是对它们执行某些操作,然后返回包含更改的新列表。在你的案例列表中:循环内部的List(x)实际上并没有做任何你能看到的事情,除非你打印出来。

答案 3 :(得分:1)

鉴于 val listOfInts = List(1,2,3) ,您希望最终结果为 List(List(1),List(2),List(3))

我能想到的另一个好方法是滑动(通过在它们上面传递“滑动窗口”来组合固定大小块中的元素)

scala> val listOfInts = List(1,2,3)
listOfInts: List[Int] = List(1, 2, 3)

scala> listOfInts.sliding(1)
res6: Iterator[List[Int]] = non-empty iterator

scala> listOfInts.sliding(1).toList
res7: List[List[Int]] = List(List(1), List(2), List(3))

// If pass 2 in sliding, it will be like
scala> listOfInts.sliding(2).toList
res8: List[List[Int]] = List(List(1, 2), List(2, 3))

有关滑动的更多信息,您可以在 scala.collection.IterableLike 中阅读滑动

答案 4 :(得分:0)

在scala中你有两个(三个,如@som-snytt所示)选项 - 选择一个可变集合(如Buffer):

scala> val xs = collection.mutable.Buffer(1)
// xs: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1)

scala> xs += 2
// res10: xs.type = ArrayBuffer(1, 2)

scala> xs += 3
// res11: xs.type = ArrayBuffer(1, 2, 3)

正如您所看到的,它就像您在Java中使用列表一样工作。您拥有的另一个选项,实际上是非常鼓励的,是选择在功能上处理列表,就是这样,您需要一些功能并将其应用于集合的每个元素:

scala> val ys = List(1,2,3,4).map(x => x + 1)
// ys: List[Int] = List(2, 3, 4, 5)

scala> def isEven(x: Int) = x % 2 == 0
// isEven: (x: Int)Boolean

scala> val zs = List(1,2,3,4).map(x => x * 10).filter(isEven)
// zs: List[Int] = List(10, 20, 30, 40) 

答案 5 :(得分:0)

// input: List(1,2,3)
// expected output: List(List(1), List(2), List(3))

val myList: List[Int] = List(1,2,3)
val currentResult = List()

def buildIteratively(input: List[Int], currentOutput: List[List[Int]]): List[List[Int]] = input match {
  case Nil => currentOutput
  case x::xs => buildIteratively(xs, List(x) :: currentOutput)
}

val result = buildIteratively(myList, currentResult).reverse

答案 6 :(得分:-2)

你在问题​​中说该列表是不可变的,所以你意识到你不能改变它! Scala列表上的所有操作都会返回列表。顺便说一句,即使在Java中使用foreach来填充集合也被认为是一种不好的做法。用例的Scala成语是:

list ::: listOfInts

更短,更清晰,更实用,更具惯用性和更容易推理(可变性使事情更多"复杂"特别是在编写lambda表达式时,因为它打破了纯函数的语义)。没有充分的理由给你一个不同的答案。

如果您想要可变性(可能出于性能目的),请使用可变集合,例如ArrayBuffer