给定 Scala 中的一个集合,我想遍历这个集合,对于每个对象,我想从 0 到多个元素发出(产量),这些元素应该连接到一个新的集合中。
例如,我期待这样的事情:
val input = Range(0, 15)
val output = input.somefancymapfunction((x) => {
if (x % 3 == 0)
yield(s"${x}/3")
if (x % 5 == 0)
yield(s"${x}/5")
})
建一个
output
将包含的集合(0/3, 0/5, 3/3, 5/5, 6/3, 9/3, 10/5, 12/3)
基本上,我想要一个超集
filter
(1 → 0..1) 和 map
(1 → 1) 允许做:映射 (1 → 0..n)。我尝试过的解决方案
势在必行的解决方案
显然,可以在非功能性的方式中这样做,例如:
var output = mutable.ListBuffer()
input.foreach((x) => {
if (x % 3 == 0)
output += s"${x}/3"
if (x % 5 == 0)
output += s"${x}/5"
})
平面图解决方案
我知道
flatMap
,但又一次,要么:1) 如果我们谈论任意数量的输出元素,就会变得非常难看:
val output = input.flatMap((x) => {
val v1 = if (x % 3 == 0) {
Some(s"${x}/3")
} else {
None
}
val v2 = if (x % 5 == 0) {
Some(s"${x}/5")
} else {
None
}
List(v1, v2).flatten
})
2) 需要在其中使用可变集合:
val output = input.flatMap((x) => {
val r = ListBuffer[String]()
if (x % 3 == 0)
r += s"${x}/3"
if (x % 5 == 0)
r += s"${x}/5"
r
})
这实际上比从一开始就使用可变集合更糟糕,或者
3) 需要大的逻辑检修:
val output = input.flatMap((x) => {
if (x % 3 == 0) {
if (x % 5 == 0) {
List(s"${x}/3", s"${x}/5")
} else {
List(s"${x}/3")
}
} else if (x % 5 == 0) {
List(s"${x}/5")
} else {
List()
}
})
也就是说,恕我直言,它看起来也很丑陋,需要复制生成代码。
滚动你自己的 map 功能
最后但并非最不重要的是,我可以推出自己的那种功能:
def myMultiOutputMap[T, R](coll: TraversableOnce[T], func: (T, ListBuffer[R]) => Unit): List[R] = {
val out = ListBuffer[R]()
coll.foreach((x) => func.apply(x, out))
out.toList
}
几乎可以像我想要的那样使用:
val output = myMultiOutputMap[Int, String](input, (x, out) => {
if (x % 3 == 0)
out += s"${x}/3"
if (x % 5 == 0)
out += s"${x}/5"
})
我真的忽略了一些东西,标准 Scala 集合库中没有这样的功能吗?
类似问题
这个问题与Can I yield or map one element into many in Scala?有些相似。 — 但那个问题讨论了 1 个元素 → 3 个元素的映射,我想要 1 个元素 → 任意数量的元素映射。
最后说明
请注意,这不是关于除法/除数的问题,这些条件仅用于说明目的。
最佳答案
与其为每个除数设置一个单独的 case,不如将它们放在一个容器中并在 a 中迭代它们以进行理解:
val output = for {
n <- input
d <- Seq(3, 5)
if n % d == 0
} yield s"$n/$d"
或者等效地在
collect
嵌套在 flatMap
:val output = input.flatMap { n =>
Seq(3, 5).collect {
case d if n % d == 0 => s"$n/$d"
}
}
在不同情况可能具有不同逻辑的更一般情况下,您可以将每个情况放在单独的部分函数中并迭代部分函数:
val output = for {
n <- input
f <- Seq[PartialFunction[Int, String]](
{case x if x % 3 == 0 => s"$x/3"},
{case x if x % 5 == 0 => s"$x/5"})
if f.isDefinedAt(n)
} yield f(n)
关于Scala:转换一个集合,每次迭代产生 0..many 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35601077/