我可以 return 使用 for-yield 语法在 Scala 中映射集合吗?

Can I return Map collection in Scala using for-yield syntax?

我是 Scala 的新手,所以希望你能容忍这个问题,以防你觉得它很菜:)

我写了一个函数,return使用 yield 语法将元素序列化为:

def calculateSomeMetrics(names: Seq[String]): Seq[Long] = {
  for (name <- names) yield {
    // some auxiliary actions
    val metrics = somehowCalculateMetrics()
    metrics
  }
}

现在我需要将其修改为 return 一个映射以针对每个计算值保留原始名称:

def calculateSomeMetrics(names: Seq[String]): Map[String, Long] = { ... }

我尝试使用相同的 yield 语法,但生成的是元组而不是单个元素:

def calculateSomeMetrics(names: Seq[String]): Map[String, Long] = {
  for (name <- names) yield {
    // Everything is the same as before
    (name, metrics)
  }
}

但是,编译器会根据编译器错误消息

解释它 Seq[(String, Long)]
type mismatch;
  found   : Seq[(String, Long)]
  required: Map[String, Long]

所以我想知道,实现这样的事情的 "canonical Scala way" 是什么?

或者:

def calculateSomeMetrics(names: Seq[String]): Map[String, Long] = {
  (for (name <- names) yield {
    // Everything is the same as before
    (name, metrics)
  }).toMap
}

或者:

names.map { name =>
  // doStuff
  (name, metrics)
}.toMap

创建不同集合类型的有效方法是使用 scala.collection.breakOut。它适用于 Maps 并且也适用于理解:

import scala.collection.breakOut

val x: Map[String, Int] = (for (i <- 1 to 10) yield i.toString -> i)(breakOut)

x: Map[String,Int] = Map(8 -> 8, 4 -> 4, 9 -> 9, 5 -> 5, 10 -> 10, 6 -> 6, 1 -> 1, 2 -> 2, 7 -> 7, 3 -> 3)

在你的情况下它也应该有效:

import scala.collection.breakOut

def calculateSomeMetrics(names: Seq[String]): Map[String, Long] = {
  (for (name <- names) yield {
    // Everything is the same as before
    (name, metrics)
  })(breakOut)
}

toMap 解决方案的比较:在 toMap 之前创建了 Tuple2 的中间 Seq(在某些情况下,顺便说一下,它也可能是 Map ) 并由此创建 Map,而 breakOut 省略了中间 Seq 创建并直接创建 Map 而不是中间 Seq.

通常这在内存或 CPU 使用(+ GC 压力)方面没有太大差异,但有时这些事情很重要。

这里有几个链接是其他人指向我的,或者我后来设法找到的,只是将它们组合成一个答案以供我将来参考。

  • breakOut - Michał in
  • 建议
  • toMap - 在 this thread
  • 关于 breakOut 工作原理的深刻解释 - 在 this answer