Scala 中的不可变向量或列表生成器
Immutable Vector or List Builder in Scala
我 运行 需要根据某些条件将某些条目提取到相应的列表中。这是我的代码
var keys = Vector[String]()
var data = Vector[String]()
for ((k, v) <- myMap) {
if (v.endsWith("abc")) { keys = keys :+ v }
if (v.endsWith("xyz")) { data = data :+ v }
}
在不将 keys 和 data 设为 var 的情况下实现此逻辑的最佳方法是什么? Scala 中有不可变列表生成器这样的东西吗?
例如番石榴中的ImmutableList.Builder(Java)https://google.github.io/guava/releases/21.0/api/docs/com/google/common/collect/ImmutableList.Builder.html
使用 foldLeft 怎么样?
val map: Map[Int, String] = Map(
1 -> "abc",
2 -> "xyz",
3 -> "abcxyz",
4 -> "xyzabc"
)
val r = map.foldLeft((Seq.empty[String], Seq.empty[String])) {
case ((keys, data), (k, v)) =>
if (v.endsWith("abc")) {
(keys :+ v, data)
}
else if (v.endsWith("xyz")) {
(keys, data :+ v)
}
else {
(keys, data)
}
}
r match {
case (keys, data) =>
println(s"keys: $keys")
println(s"data: $data")
}
如果您被迫使用 var
或可变集合(超出您的优化需要),您可能没有正确考虑问题。
假设我们有一张地图m
:
Map(1 -> "abc", 2 -> "xyz")
现在,我们可以使用递归来解决这个问题(我在这里以尾递归的形式完成):
type Keys = Vector[String]
type Data = Vector[String]
def keyData(m: Map[Int, String]): (Keys, Data) = {
def go(keys: Keys, data: Data, m: List[(Int, String)]): (Keys, Data) =
m match {
case (k, v) :: ks if v endsWith("abc") =>
go(v +: keys, data, ks)
case (k, v) :: ks if v endsWith("xyz") =>
go(keys, v +: data, ks)
case k :: ks =>
go(keys, data, ks)
case _ => (keys, data)
}
go(Vector.empty[String], Vector.empty[String], m.toList)
}
这将获取一个映射并生成一对向量,其中包含与您列出的谓词匹配的字符串数据。现在,假设我们想要将地图元素抽象并划分为满足任意两个谓词 p: Int => Boolean
或 q: Int => Boolean
的向量。然后,我们将得到如下所示的内容:
type Keys = Vector[String]
type Data = Vector[String]
def keyData(m: Map[Int, String], p: Int => Boolean, q: Int => Boolean): (Keys, Data) = {
def go(keys: Keys, data: Data, m: List[(Int, String)]): (Keys, Data) =
m match {
case (k, v) :: ks if p(v) =>
go(v +: keys, data, ks)
case (k, v) :: ks if q(v) =>
go(keys, v +: data, ks)
case k :: ks =>
go(keys, data, ks)
case _ => (keys, data)
}
go(Vector.empty[String], Vector.empty[String], m.toList)
}
现在,我们可以对任何键和值类型 K
和 V
进行抽象:
def partitionMapBy[K, V](m: Map[K, V], p: V => Boolean, q: V => Boolean): (Vector[V], Vector[V]) = {
def go(keys: Vector[V], data: Vector[V], m: List[(K, V)]): (Vector[V], Vector[V]) =
m match {
case (k, v) :: ks if p(v) =>
go(v +: keys, data, ks)
case (k, v) :: ks if q(v) =>
go(keys, v +: data, ks)
case k :: ks =>
go(keys, data, ks)
case _ => (keys, data)
}
go(Vector.empty[V], Vector.empty[V], m.toList)
}
您会注意到这里的递归并没有什么特别的地方。这意味着我们可以使用折叠来完成同样的事情。这是使用 foldLeft
:
的实现
def partitionMapBy[K, V](m: Map[K, V])(p: V => Boolean)(q: V => Boolean): (Vector[V], Vector[V]) =
m.foldLeft[(Vector[V], Vector[V])]((Vector.empty[V], Vector.empty[V])) {
case (acc @ (keys: Vector[V], data: Vector[V]), (_, v: V)) =>
if(p(v)) (v +: keys, data)
else if(q(v)) (keys, v +: data)
else acc
}
你可以看到,对于 m
,we get the this,如果你让 p
为 _ endsWith("abc")
,q
为 _ endsWith("xyz")
,那么你就会得到你想要的。
`
您可以根据需要对值进行分区。
val (keys, notKeys) = myMap.values.partition(_.endsWith("abc"))
val (data, _) = notKeys.partition(_.endsWith("xyz"))
您的 keys
和 data
collections 将是 List[String]
而不是 Vector
,但如果需要,这很容易 mod。
每个 Scala 集合都带有仅附加构建器:
val keysB, dataB = Vector.newBuilder[String]
for ((k, v) <- myMap) {
if (v.endsWith("abc")) { keysB += v }
if (v.endsWith("xyz")) { dataB += v }
}
val keys = keysB.result()
val data = dataB.result()
我 运行 需要根据某些条件将某些条目提取到相应的列表中。这是我的代码
var keys = Vector[String]()
var data = Vector[String]()
for ((k, v) <- myMap) {
if (v.endsWith("abc")) { keys = keys :+ v }
if (v.endsWith("xyz")) { data = data :+ v }
}
在不将 keys 和 data 设为 var 的情况下实现此逻辑的最佳方法是什么? Scala 中有不可变列表生成器这样的东西吗?
例如番石榴中的ImmutableList.Builder(Java)https://google.github.io/guava/releases/21.0/api/docs/com/google/common/collect/ImmutableList.Builder.html
使用 foldLeft 怎么样?
val map: Map[Int, String] = Map(
1 -> "abc",
2 -> "xyz",
3 -> "abcxyz",
4 -> "xyzabc"
)
val r = map.foldLeft((Seq.empty[String], Seq.empty[String])) {
case ((keys, data), (k, v)) =>
if (v.endsWith("abc")) {
(keys :+ v, data)
}
else if (v.endsWith("xyz")) {
(keys, data :+ v)
}
else {
(keys, data)
}
}
r match {
case (keys, data) =>
println(s"keys: $keys")
println(s"data: $data")
}
如果您被迫使用 var
或可变集合(超出您的优化需要),您可能没有正确考虑问题。
假设我们有一张地图m
:
Map(1 -> "abc", 2 -> "xyz")
现在,我们可以使用递归来解决这个问题(我在这里以尾递归的形式完成):
type Keys = Vector[String]
type Data = Vector[String]
def keyData(m: Map[Int, String]): (Keys, Data) = {
def go(keys: Keys, data: Data, m: List[(Int, String)]): (Keys, Data) =
m match {
case (k, v) :: ks if v endsWith("abc") =>
go(v +: keys, data, ks)
case (k, v) :: ks if v endsWith("xyz") =>
go(keys, v +: data, ks)
case k :: ks =>
go(keys, data, ks)
case _ => (keys, data)
}
go(Vector.empty[String], Vector.empty[String], m.toList)
}
这将获取一个映射并生成一对向量,其中包含与您列出的谓词匹配的字符串数据。现在,假设我们想要将地图元素抽象并划分为满足任意两个谓词 p: Int => Boolean
或 q: Int => Boolean
的向量。然后,我们将得到如下所示的内容:
type Keys = Vector[String]
type Data = Vector[String]
def keyData(m: Map[Int, String], p: Int => Boolean, q: Int => Boolean): (Keys, Data) = {
def go(keys: Keys, data: Data, m: List[(Int, String)]): (Keys, Data) =
m match {
case (k, v) :: ks if p(v) =>
go(v +: keys, data, ks)
case (k, v) :: ks if q(v) =>
go(keys, v +: data, ks)
case k :: ks =>
go(keys, data, ks)
case _ => (keys, data)
}
go(Vector.empty[String], Vector.empty[String], m.toList)
}
现在,我们可以对任何键和值类型 K
和 V
进行抽象:
def partitionMapBy[K, V](m: Map[K, V], p: V => Boolean, q: V => Boolean): (Vector[V], Vector[V]) = {
def go(keys: Vector[V], data: Vector[V], m: List[(K, V)]): (Vector[V], Vector[V]) =
m match {
case (k, v) :: ks if p(v) =>
go(v +: keys, data, ks)
case (k, v) :: ks if q(v) =>
go(keys, v +: data, ks)
case k :: ks =>
go(keys, data, ks)
case _ => (keys, data)
}
go(Vector.empty[V], Vector.empty[V], m.toList)
}
您会注意到这里的递归并没有什么特别的地方。这意味着我们可以使用折叠来完成同样的事情。这是使用 foldLeft
:
def partitionMapBy[K, V](m: Map[K, V])(p: V => Boolean)(q: V => Boolean): (Vector[V], Vector[V]) =
m.foldLeft[(Vector[V], Vector[V])]((Vector.empty[V], Vector.empty[V])) {
case (acc @ (keys: Vector[V], data: Vector[V]), (_, v: V)) =>
if(p(v)) (v +: keys, data)
else if(q(v)) (keys, v +: data)
else acc
}
你可以看到,对于 m
,we get the this,如果你让 p
为 _ endsWith("abc")
,q
为 _ endsWith("xyz")
,那么你就会得到你想要的。
`
您可以根据需要对值进行分区。
val (keys, notKeys) = myMap.values.partition(_.endsWith("abc"))
val (data, _) = notKeys.partition(_.endsWith("xyz"))
您的 keys
和 data
collections 将是 List[String]
而不是 Vector
,但如果需要,这很容易 mod。
每个 Scala 集合都带有仅附加构建器:
val keysB, dataB = Vector.newBuilder[String]
for ((k, v) <- myMap) {
if (v.endsWith("abc")) { keysB += v }
if (v.endsWith("xyz")) { dataB += v }
}
val keys = keysB.result()
val data = dataB.result()