我需要有效地循环一个固定的序列,从任何一个元素开始,而不是始终从头开始 我一直在探索的解决方案是:
val directions = List("north", "east", "south", "west")
val cycle = Stream.continually(directions).flatten
cycle dropWhile (_ != "south") take 12 foreach println
由于我在一个热循环中使用它,一遍又一遍地开始,并且实际的序列非常冗长,我担心dropWhile
将花费我很多。
有没有办法可以加快速度呢?
我是以正确的方式做到的吗?
修改
给出了一些好的答案,表明我不应该使用Stream
。现在的核心问题是:如何避免使用dropWhile
或类似功能?
答案 0 :(得分:3)
以下示例对固定序列的值进行索引,以避免每次启动新流时遍历列表。
package rando
object Faster extends App {
val directions = Vector("north", "east", "south", "west")
val index = directions.zipWithIndex.map { case (d, i) => d -> i }.toMap
def streamWithStart(start: String): Stream[String] =
directions.drop(index(start)).toStream.append(Stream.continually(directions).flatten)
streamWithStart("east") take 10 reduce(_+" "+_) foreach print; println()
streamWithStart("north") take 10 reduce(_+" "+_) foreach print; println()
streamWithStart("west") take 10 reduce(_+" "+_) foreach print; println()
streamWithStart("south") take 10 reduce(_+" "+_) foreach print; println()
}
答案 1 :(得分:2)
这是一个隐式类,允许您在任何.looped
上调用Traversable
方法。我相信你会发现Iterator
返回的效率比展平Stream.continually
的效果要高得多。
import scala.reflect.ClassTag
/** Turns a Traversable into an infinite loop. */
implicit class TraversableLooper[A:ClassTag]( val items:Traversable[A] ) {
def looped:Iterator[A] =
if ( items.isEmpty )
sys error "<empty>.looped"
else
new Iterator[A] {
val array = items.toArray
val numItems = array.length
var i = -1
def hasNext = true
def next = {
i += 1
if ( i == numItems ) i = 0
array(i)
}
}
}
这里正在使用:
scala> val directions = List("north", "east", "south", "west")
directions: List[String] = List(north, east, south, west)
scala> directions.looped dropWhile (_ != "south") take 12 foreach println
south
west
north
east
south
west
north
east
south
west
north
east
此外,在进行循环之前尽可能多地执行,例如dropWhile
:
( directions.looped dropWhile (_ != "south") take 4 ).toSeq.looped
答案 2 :(得分:1)
您应该使用Iterator
而不是Stream
。 Stream
让一切都记忆犹新,变得越来越大。
scala> val directions = List("north", "east", "south", "west")
directions: List[String] = List(north, east, south, west)
scala> val cycle = Iterator.continually(directions).flatten
cycle: Iterator[String] = non-empty iterator
scala> cycle dropWhile (_ != "south") take 2 foreach println
south
west
scala> cycle dropWhile (_ != "east") take 2 foreach println
east
south
答案 3 :(得分:1)
另一种方法,其中directions
键值south
被移动到列表的头部,然后我们在移位列表上迭代4
次,
implicit class AddCycleToList[A](val list: List[A]) extends AnyVal {
def cycle(times: Int, from: A): Unit = {
val shiftBy = list span { _ != from }
val shifted = shiftBy._2 ++ shiftBy._1
for (i <- 1 to times) shifted foreach println
}
}
因此
scala> val directions = List("north", "east", "south", "west")
directions: List[String] = List(north, east, south, west)
scala> directions.cycle(4, "south")
south
west
north
east
south
west
north
east
south
west
north
east
south
west
north
east
答案 4 :(得分:1)
为避免尽可能重新计算:
val cycle = Stream.continually(directions).flatten
val cachedStreams = collections.mutable.Map.empty[String, Stream[String]]
def cycleStartingWith(direction: String) =
cachedStreams.getOrElseUpdate(direction, cycle.dropWhile(_ != direction))
然而,这不适用于Iterator
s;在这种情况下,我会对Dave Rando的答案进行修改。