我有一个项目列表,我为每个项目计算一个值。计算这个值有点计算密集,所以我想尽可能地减少它。
我需要实现的算法是:
我有一个值X
每个项目
一个。计算它的值,如果它是< 0完全忽略它
湾if(value> 0)&& (值< X) 返回对(项目,值)
返回列表中的所有(项目,值)对(值> 0),理想情况下按值排序
为了使它更清晰一点,只有在没有任何项的值小于X的情况下才会发生第3步。在步骤2中,当我们遇到小于X的第一个项时,我们不应该计算其余项而只返回那个项目(我们显然可以在Set()中单独返回它以匹配返回类型)。
我目前的代码如下:
val itemValMap = items.foldLeft(Map[Item, Int)]()) {
(map : Map[Item, Int], key : Item) =>
val value = computeValue(item)
if ( value >= 0 ) //we filter out negative ones
map + (key -> value)
else
map
}
val bestItem = itemValMap.minBy(_._2)
if (bestItem._2 < bestX)
{
List(bestItem)
}
else
{
itemValMap.toList.sortBy(_._2)
}
然而,这段代码正在做的是计算列表中的所有值并选择最好的值,而不是停下来找到“更好”的值。我怀疑我必须以某种方式使用Streams来实现这个目标吗?
答案 0 :(得分:2)
好的,我不确定你的整个设置是怎么样的,但我试着准备一个能反映你情况的最小例子。
然后是:
object StreamTest {
case class Item(value : Int)
def createItems() = List(Item(0),Item(3),Item(30),Item(8),Item(8),Item(4),Item(54),Item(-1),Item(23),Item(131))
def computeValue(i : Item) = { Thread.sleep(3000); i.value * 2 - 2 }
def process(minValue : Int)(items : Seq[Item]) = {
val stream = Stream(items: _*).map(item => item -> computeValue(item)).filter(tuple => tuple._2 >= 0)
stream.find(tuple => tuple._2 < minValue).map(List(_)).getOrElse(stream.sortBy(_._2).toList)
}
}
每次计算需要3秒钟。现在让我们看看它是如何工作的:
val items = StreamTest.createItems()
val result = StreamTest.process(2)(items)
result.foreach(r => println("Original: " + r._1 + " , calculated: " + r._2))
给出:
[info] Running Main
Original: Item(3) , calculated: 4
Original: Item(4) , calculated: 6
Original: Item(8) , calculated: 14
Original: Item(8) , calculated: 14
Original: Item(23) , calculated: 44
Original: Item(30) , calculated: 58
Original: Item(54) , calculated: 106
Original: Item(131) , calculated: 260
[success] Total time: 31 s, completed 2013-11-21 15:57:54
由于没有小于2的值,我们得到了一个按计算值排序的列表。请注意,缺少两对,因为计算值小于0并被过滤掉。
好的,现在让我们尝试使用不同的最小截止点:
val result = StreamTest.process(5)(items)
给出了:
[info] Running Main
Original: Item(3) , calculated: 4
[success] Total time: 7 s, completed 2013-11-21 15:55:20
好,它返回的列表中只有一个项目,第一个值(原始列表中的第二个项目)小于“最小”值,不小于0.
我希望上面的例子很容易适应您的需求......
答案 1 :(得分:2)
避免计算不需要的值的一种简单方法是使用view
方法使您的集合变得懒惰:
val weigthedItems = items.view.map{ i => i -> computeValue(i) }.filter(_._2 >= 0 )
weigthedItems.find(_._2 < X).map(List(_)).getOrElse(weigthedItems.sortBy(_._2))
例如,这里是REPL中的一个测试:
scala> :paste
// Entering paste mode (ctrl-D to finish)
type Item = String
def computeValue( item: Item ): Int = {
println("Computing " + item)
item.toInt
}
val items = List[Item]("13", "1", "5", "-7", "12", "3", "-1", "15")
val X = 10
val weigthedItems = items.view.map{ i => i -> computeValue(i) }.filter(_._2 >= 0 )
weigthedItems.find(_._2 < X).map(List(_)).getOrElse(weigthedItems.sortBy(_._2))
// Exiting paste mode, now interpreting.
Computing 13
Computing 1
defined type alias Item
computeValue: (item: Item)Int
items: List[String] = List(13, 1, 5, -7, 12, 3, -1, 15)
X: Int = 10
weigthedItems: scala.collection.SeqView[(String, Int),Seq[_]] = SeqViewM(...)
res27: Seq[(String, Int)] = List((1,1))
正如您所看到的,computeValue仅被调用到第一个值&lt; X(即最多1
)