【问题标题】:Can I count occurrence in 1 loop in this Scala fragment?我可以在这个 Scala 片段中计算 1 个循环中的出现次数吗?
【发布时间】:2010-11-25 09:23:20
【问题描述】:

我有一段简单的 Scala 代码。我按顺序循环遍历字符串列表,我想计算每个字符串在列表 r 中作为元组 (String, Int) 收集的每个字符串的出现次数。主函数中的部分应该保留(所以没有groupBy 或其他东西)。我的问题是关于更新功能:

现在我先做一个find,如果它不存在,然后将一个新元组添加到 r。如果它确实存在,我会遍历 r 并更新匹配字符串的计数器。

是否可以修改更新功能以提高效率? r 是否可以在一次迭代中更新(如果不存在则添加,如果存在则更新计数器)?

谢谢

var r = List[(String, Int)]() // (string, count)

def update(s: String, l: List[(String, Int)]) : List[(String, Int)] = {
  if (r.find(a => a._1  == s) == None) {
    (s, 1) :: r // add a new item if it does not exist
  } else {
    for (b <- l) yield {
      if (b._1 == s) {
        (b._1, b._2 + 1) // update counter if exists
      } else {
        b // just yield if no match
      }
    }
  }
}

def main(args : Array[String]) : Unit = {
  val l = "A" :: "B" :: "A" :: "C" :: "A" :: "B" :: Nil

  for (s <- l) r = update(s, r)

  r foreach println
}

【问题讨论】:

  • 我不明白您对groupBy 的反对意见是什么。好像是说main定义中的代码可能不会被解决方案改变;这也可能意味着l 可能不会更改。那么,你能澄清一下吗?
  • 主要是因为我也想使用惰性集合。或者您也可以将groupBy 用于惰性集合和Streams(或Source.getLines)?如果有,请举个例子。

标签: scala


【解决方案1】:

我建议你选择函数式风格并使用 Scala 集合的强大功能:

ss.groupBy(identity).mapValues(_.size)

【讨论】:

  • s =&gt; s 预定义为identity
  • 这个解决方案经常出现,它真的应该放在核心库中。我会调用方法tally
  • 我同意,这是一个很好的解决方案,但我正在寻找 groupBy 的替代方案,也可用于惰性集合,因此我选择了 Heiko 的另一个答案。
  • 我将它的实现作为 Traversable 的标准皮条客,我几乎将其包含在我所有的项目中。我称之为“countBy”,也有方便的方法“sumBy”、“minBy”、“maxBy”等。在它们之间,它们满足了我的很多数据缩减需求。
【解决方案2】:

如果您不想使用 groupBy 或使用惰性集合(流),这可能是要走的路:

ss.foldLeft(Map[String, Int]())((m, s) => m + (s -> (m.getOrElse(s, 0) + 1)))

【讨论】:

  • 这确实返回了Map 而不是List,并且不是update 的替代品(尽管我看到@JanWillem 已经接受了你的答案:)。
  • 没错,返回类型不同,但确实符合我想要达到的目标:“...我要统计每个String的出现...”
【解决方案3】:

类似的方法也可以:

val l = List("A","B","A","C","A","B")

l.foldLeft(Map[String,Int]()) {
  case (a: Map[String, Int], s: String) => {
    a + (s -> (1 + a.getOrElse(s, 0)))
  }
}

res3: scala.collection.immutable.Map[String,Int] = Map((A,3), (B,2), (C,1))

【讨论】:

    【解决方案4】:

    您目前的解决方案非常慢,主要是因为选择r 作为List。但是,至少可以改进在更新的情况下您在整个列表中的迭代。我会这样写

    def update(s: String, l: List[(String, Int)]) : List[(String, Int)] = {
      l span (_._1 != s) match {
        case (before, (`s`, count) :: after) => before ::: (s, count + 1) :: after
        case _ => (s, 1) :: l
      }
    }
    

    使用span 可以避免搜索列表两次。此外,我们只需附加after,无需再次遍历它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-20
      • 2017-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-24
      • 1970-01-01
      • 2014-01-12
      相关资源
      最近更新 更多