计算所有值或停止并返回最佳值(如果找到)

时间:2013-11-21 10:41:24

标签: list scala map stream

我有一个项目列表,我为每个项目计算一个值。计算这个值有点计算密集,所以我想尽可能地减少它。

我需要实现的算法是:

  1. 我有一个值X

  2. 每个项目

    一个。计算它的值,如果它是< 0完全忽略它

    湾if(value> 0)&& (值< X)       返回对(项目,值)

  3. 返回列表中的所有(项目,值)对(值> 0),理想情况下按值排序

  4. 为了使它更清晰一点,只有在没有任何项的值小于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来实现这个目标吗?

2 个答案:

答案 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