展平 Scala 中的元组列表?
Flatten a list of tuples in Scala?
我本以为元组列表可以很容易地展平:
scala> val p = "abcde".toList
p: List[Char] = List(a, b, c, d, e)
scala> val q = "pqrst".toList
q: List[Char] = List(p, q, r, s, t)
scala> val pq = p zip q
pq: List[(Char, Char)] = List((a,p), (b,q), (c,r), (d,s), (e,t))
scala> pq.flatten
但是,却发生了这种情况:
<console>:15: error: No implicit view available from (Char, Char) => scala.collection.GenTraversableOnce[B].
pq.flatten
^
我可以通过以下方式完成工作:
scala> (for (x <- pq) yield List(x._1, x._2)).flatten
res1: List[Char] = List(a, p, b, q, c, r, d, s, e, t)
但是我不明白错误信息。我的替代解决方案似乎很复杂且效率低下。
该错误消息是什么意思,为什么我不能简单地展平元组列表?
如果找不到隐式转换,您可以显式提供。
pq.flatten {case (a,b) => List(a,b)}
如果在整个代码中多次执行此操作,那么您可以通过将其设为隐式来保存一些样板文件。
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala> implicit def flatTup[T](t:(T,T)): List[T]= t match {case (a,b)=>List(a,b)}
flatTup: [T](t: (T, T))List[T]
scala> pq.flatten
res179: List[Char] = List(a, p, b, q, c, r, d, s, e, t)
jwvh 的回答很好地涵盖了 "coding" 对您问题的解决方案,因此我不打算对此进行更多详细介绍。我唯一想补充的是澄清为什么需要你和 jwvh 找到的解决方案。
如 Scala 库中所述,Tuple2
((,)
转换为)是:
A tuple of 2 elements; the canonical representation of a Product2
.
并跟进:
Product2
is a cartesian product of 2 components.
...Tuple2[T1,T2]
代表哪个means:
The set of all possible pairs of elements whose components are members of two sets (all elements in T1
and T2
respectively).
另一方面,A List[T]
表示 T
元素的有序集合。
这实际上意味着 没有绝对的方法可以将任何可能的 Tuple2[T1,T2]
转换为 List[T]
,仅仅因为 T1
和 T2
可能不同。例如,采用以下元组:
val tuple = ("hi", 5)
这样的元组怎么能被压平? 5
应该变成 String
吗?或者可能只是压平到 List[Any]
?虽然这两种解决方案都可以使用,但它们 围绕类型系统工作 ,因此它们未按设计编码在 Tuple
API 中。
所有这一切都归结为这样一个事实,即在这种情况下没有默认的隐式视图,您必须自己提供一个,因为 jwvh 并且您已经知道了。
我们最近需要这样做。在说明我们的解决方案之前,请允许我简要解释一下用例。
用例
给定一个项目池(我将其称为 T
类型),我们想要针对池中的所有其他项目对每个项目进行评估。这些比较的结果是 Set
个 失败的评估 ,我们将其表示为所述评估中的左项和右项的元组:(T, T)
。
完成这些评估后,我们可以将 Set[(T, T)]
扁平化为另一个 Set[T]
,突出显示所有未通过任何比较的项目。
解决方案
我们的解决方案是弃牌:
val flattenedSet =
set.foldLeft(Set[T]())
{ case (acc, (x, y)) => acc + x + y }
这从一个空集(foldLeft
的初始参数)开始作为 累加器。
然后,这里对consumedSet[(T, T)]
(命名为set
)中的每个元素,传递fold函数:
- 累加器的最后一个值 (
acc
),并且
- 该元素的
(T, T)
元组,case
解构为 x
和 y
.
我们的fold函数然后是returns acc + x + y
,其中returns是一个包含累加器中除了x
和y
之外的所有元素的集合.该结果作为累加器传递给下一次迭代——因此,它累加每个元组中的所有值。
为什么不 List
s?
我特别欣赏这个解决方案,因为它避免了在进行扁平化时创建中间 List
——相反,它在构建新的 Set[T]
.
时直接解构每个元组
我们也可以将我们的评估代码更改为 return List[T]
s,其中包含每个失败评估中的左侧和右侧项目——然后 flatten
就可以工作™。但是我们认为元组更准确地表示了我们要进行评估的目的——特别是一个项目与另一个项目的对比,而不是可以想象地表示任意数量项目的开放式类型。
我本以为元组列表可以很容易地展平:
scala> val p = "abcde".toList
p: List[Char] = List(a, b, c, d, e)
scala> val q = "pqrst".toList
q: List[Char] = List(p, q, r, s, t)
scala> val pq = p zip q
pq: List[(Char, Char)] = List((a,p), (b,q), (c,r), (d,s), (e,t))
scala> pq.flatten
但是,却发生了这种情况:
<console>:15: error: No implicit view available from (Char, Char) => scala.collection.GenTraversableOnce[B].
pq.flatten
^
我可以通过以下方式完成工作:
scala> (for (x <- pq) yield List(x._1, x._2)).flatten
res1: List[Char] = List(a, p, b, q, c, r, d, s, e, t)
但是我不明白错误信息。我的替代解决方案似乎很复杂且效率低下。
该错误消息是什么意思,为什么我不能简单地展平元组列表?
如果找不到隐式转换,您可以显式提供。
pq.flatten {case (a,b) => List(a,b)}
如果在整个代码中多次执行此操作,那么您可以通过将其设为隐式来保存一些样板文件。
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala> implicit def flatTup[T](t:(T,T)): List[T]= t match {case (a,b)=>List(a,b)}
flatTup: [T](t: (T, T))List[T]
scala> pq.flatten
res179: List[Char] = List(a, p, b, q, c, r, d, s, e, t)
jwvh 的回答很好地涵盖了 "coding" 对您问题的解决方案,因此我不打算对此进行更多详细介绍。我唯一想补充的是澄清为什么需要你和 jwvh 找到的解决方案。
如 Scala 库中所述,Tuple2
((,)
转换为)是:
A tuple of 2 elements; the canonical representation of a
Product2
.
并跟进:
Product2
is a cartesian product of 2 components.
...Tuple2[T1,T2]
代表哪个means:
另一方面,The set of all possible pairs of elements whose components are members of two sets (all elements in
T1
andT2
respectively).
A List[T]
表示 T
元素的有序集合。
这实际上意味着 没有绝对的方法可以将任何可能的 Tuple2[T1,T2]
转换为 List[T]
,仅仅因为 T1
和 T2
可能不同。例如,采用以下元组:
val tuple = ("hi", 5)
这样的元组怎么能被压平? 5
应该变成 String
吗?或者可能只是压平到 List[Any]
?虽然这两种解决方案都可以使用,但它们 围绕类型系统工作 ,因此它们未按设计编码在 Tuple
API 中。
所有这一切都归结为这样一个事实,即在这种情况下没有默认的隐式视图,您必须自己提供一个,因为 jwvh 并且您已经知道了。
我们最近需要这样做。在说明我们的解决方案之前,请允许我简要解释一下用例。
用例
给定一个项目池(我将其称为 T
类型),我们想要针对池中的所有其他项目对每个项目进行评估。这些比较的结果是 Set
个 失败的评估 ,我们将其表示为所述评估中的左项和右项的元组:(T, T)
。
完成这些评估后,我们可以将 Set[(T, T)]
扁平化为另一个 Set[T]
,突出显示所有未通过任何比较的项目。
解决方案
我们的解决方案是弃牌:
val flattenedSet =
set.foldLeft(Set[T]())
{ case (acc, (x, y)) => acc + x + y }
这从一个空集(foldLeft
的初始参数)开始作为 累加器。
然后,这里对consumedSet[(T, T)]
(命名为set
)中的每个元素,传递fold函数:
- 累加器的最后一个值 (
acc
),并且 - 该元素的
(T, T)
元组,case
解构为x
和y
.
我们的fold函数然后是returns acc + x + y
,其中returns是一个包含累加器中除了x
和y
之外的所有元素的集合.该结果作为累加器传递给下一次迭代——因此,它累加每个元组中的所有值。
为什么不 List
s?
我特别欣赏这个解决方案,因为它避免了在进行扁平化时创建中间 List
——相反,它在构建新的 Set[T]
.
我们也可以将我们的评估代码更改为 return List[T]
s,其中包含每个失败评估中的左侧和右侧项目——然后 flatten
就可以工作™。但是我们认为元组更准确地表示了我们要进行评估的目的——特别是一个项目与另一个项目的对比,而不是可以想象地表示任意数量项目的开放式类型。