Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Scala中合并两个ITerable_Scala_Generics_Collections - Fatal编程技术网

在Scala中合并两个ITerable

在Scala中合并两个ITerable,scala,generics,collections,Scala,Generics,Collections,我想写一个merge方法,将两个iterable合并在一起。(也许merge不是描述我想要的东西的最佳词汇,但就这个问题而言,它并不重要)。我希望这种方法是通用的,可以用于不同的具体可移植性 例如,merge(Set(1,2),Set(2,3))应该返回Set(1,2,3)和 merge(List(1,2),List(2,3))应该返回List(1,2,2,3)。我做了以下天真的尝试,但是编译器抱怨res的类型:它是Iterable[Any]而不是A def merge[A <: Iter

我想写一个
merge
方法,将两个iterable合并在一起。(也许merge不是描述我想要的东西的最佳词汇,但就这个问题而言,它并不重要)。我希望这种方法是通用的,可以用于不同的具体可移植性

例如,
merge(Set(1,2),Set(2,3))
应该返回
Set(1,2,3)
merge(List(1,2),List(2,3))
应该返回
List(1,2,2,3)
。我做了以下天真的尝试,但是编译器抱怨
res
的类型:它是
Iterable[Any]
而不是
A

def merge[A <: Iterable[_]](first: A, second: A): A = {
    val res = first ++ second
    res
}

def merge[A初步而言,以下是此答案中所有代码所需的导入:

import collection.GenTraversableOnce
import collection.generic.CanBuildFrom
首先查看查看
Iterable.++
的方法签名(请注意,API记录了大多数集合,您需要单击“完整签名”以查看实际类型):

从这里,您可以直接将实例方法转换为函数:

def merge[A, B >: A, That](it: Iterable[A], that: GenTraversableOnce[B])
  (implicit bf: CanBuildFrom[Iterable[A], B, That]): That = it ++ that
分解如下:

  • [A,B>:A,那]
    -
    Iterable
    有一个类型参数
    A
    ++
    有两个类型参数
    B
    ,因此生成的函数有所有三个类型参数
    A
    B
  • it:Iterable[A]
    -该方法属于
    Iterable[A]
    ,因此我们将其作为第一个值参数
  • that:GenTraversableOnce[B])(隐式bf:CanBuildFrom[Iterable[A],B,that]):that
    -剩余的参数和类型约束直接从
    +
    的签名复制而来

让我们从代码不起作用的原因开始。首先,您无意中使用了一个缩写语法,而不是实际使用了一个绑定在更高类型上的类型

// What you wrote is equivalent to this
def merge[A <: Iterable[T] forSome {type T}](first: A, second: A): A
没有

我已经在
++
中添加了额外的类型注释,以使编译器错误更加相关。这告诉我们,因为我们没有专门为任意
++
重写
Iterable的
++
,所以我们正在使用
Iterable
的实现,这恰好发生在ta身上创建一个隐式的
CanBuildFrom
,它从
Iterable
构建到我们的
s

顺便说一句,这就是@ChrisMartin遇到的问题(这整件事对他的回答来说真是一个冗长的评论)

不幸的是,Scala没有提供这样的
CanBuildFrom
,所以看起来我们必须手动使用
CanBuildFrom

所以我们去兔子洞

让我们首先注意到,
++
实际上最初是在
TraversableLike
中定义的,因此我们可以使自定义的
合并
更通用一些

def merge[A, S[T] <: TraversableLike[T, S[T]], That](it: S[A], that: TraversableOnce[A])
  (implicit bf: CanBuildFrom[S[A], A, That]): That = ???
简而言之,
CanBuildFrom
机制允许您构建代码,以处理我们通常希望在Scala集合的继承图的不同分支之间自动转换的事实,但它会以一些复杂性和偶尔不直观的行为为代价。相应地权衡权衡

脚注

*“通用”集合,我们可以“遍历”至少“一次”,但可能不会更多,其顺序可能是连续的,也可能不是连续的,例如,可能是并行的

**与
GenTraversableOnce
相同,但不是“常规”,因为它保证顺序访问


***
TraversableLike
通过在内部强制调用
gentraversableone
上的
seq
来解决这个问题,但我觉得这是在欺骗人们,让他们在原本可能期望的情况下放弃并行性。强制调用方决定是否要放弃并行性;不要为他们无形地这么做

如果我在
val x=merge(Set(1,2,3),Set(1,2,3,4))
中运行您对merge的定义,
x
的类型是
Iterable[Int]
,但我想要
Set[Int]
。有没有办法找回最具体的类型?哎呀,我想是的。当然有可能,我会再处理一点。不,对不起,我已经走到了死胡同。谢谢你给出了详尽的答案。只有一件事:
TraversableLike
需要两个类型参数:
trait TraversableLike[+a,+Repr]
,我必须将其定义为
S[A],这就是我在运行中进行更改而没有验证它们是否真正编译的结果。我会更正它,谢谢!
def merge[A, S[T] <: Iterable[T]](first: S[A], second: S[A]): S[A] = {
  first ++ second // CanBuildFrom errors :(
}
import collection.generic.CanBuildFrom

// Cannot construct a collection of type S[A] with elements of type A 
// based on a collection of type Iterable[A]
merge0[A, S[T] <: Iterable[T], That](x: S[A], y: S[A])
  (implicit bf: CanBuildFrom[S[A], A, S[A]]): S[A] = x.++[A, S[A]](y)
def merge[A, S[T] <: TraversableLike[T, S[T]], That](it: S[A], that: TraversableOnce[A])
  (implicit bf: CanBuildFrom[S[A], A, That]): That = ???
 import collection.mutable.Builder

 def merge[A, S[T] <: TraversableLike[T, S[T]], That](it: S[A], that: TraversableOnce[A])
  (implicit bf: CanBuildFrom[S[A], A, That]): That= {
    // Getting our mutable buffer from CanBuildFrom
    val builder: Builder[A, That] = bf()
    builder ++= it
    builder ++= that
    builder.result()
  }
scala> merge(List(1, 2, 3), List(2, 3, 4))
res0: List[Int] = List(1, 2, 3, 2, 3, 4)

scala> merge(Set(1, 2, 3), Set(2, 3, 4))
res1: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)

scala> merge(List(1, 2, 3), Set(1, 2, 3))
res2: List[Int] = List(1, 2, 3, 1, 2, 3)

scala> merge(Set(1, 2, 3), List(1, 2, 3)) // Not the same behavior :(
res3: scala.collection.immutable.Set[Int] = Set(1, 2, 3)