用户Régis让 - 吉尔斯优雅地回答了我之前的问题,在那里我正在努力解决CanBuildFrom和浓缩功能(又名“皮条客我的图书馆”或“丰富我的图书馆”):
但是这次我遇到了一个更复杂的问题。
我有一个函数来实现intersectWith
的变体,用于按键交叉集合。我设法让它们像正确的收集功能一样工作:
implicit class IntersectUnionWithPimp[K, A, Repr](a: GenTraversableLike[(K, A), Repr]) {
/**
* Intersect two collections by their keys. This is identical to
* `intersectWith` except that the combiner function is passed the
* key as well as the two items to combine.
*
* @param b Other collection to intersect with.
* @param combine Function to combine values from the two collections.
*/
def intersectWithKey[B, R, That](b: GenTraversable[(K, B)])(
combine: (K, A, B) => R)(
implicit bf: CanBuildFrom[Repr, (K, R), That]): That = {
...
}
/**
* Intersect two collections by their keys. Keep the ordering of
* objects in the first collection. Use a combiner function to
* combine items in common. If either item is a multi-map, then
* for a key seen `n` times in the first collection and `m`
* times in the second collection, it will occur `n * m` times
* in the resulting collection, including all the possible
* combinations of pairs of identical keys in the two collections.
*
* @param b Other collection to intersect with.
* @param combine Function to combine values from the two collections.
*/
def intersectWith[B, R, That](b: GenTraversable[(K, B)])(
combine: (A, B) => R)(
implicit bf: CanBuildFrom[Repr, (K, R), That]): That =
a.intersectWithKey(b){ case (_, x, y) => combine(x, y) }(bf)
}
现在,我目前还有非CanBuildFrom
版本的intersectBy
和朋友,而这些我无法以CanBuildFrom
版本工作。
implicit class IntersectUnionByPimp[A](a: Traversable[A]) {
/**
* Intersect two collections by their keys, with separate key-selection
* functions for the two collections. This is identical to
* `intersectBy` except that each collection has its own key-selection
* function. This allows the types of the two collections to be
* distinct, with no common parent.
*
* @param b Other collection to intersect with.
* @param key1fn Function to select the comparison key for the first
* collection.
* @param key1fn Function to select the comparison key for the first
* collection.
* @param combine Function to combine values from the two collections.
*/
def intersectBy2[K, B, R](b: Traversable[B])(key1fn: A => K
)(key2fn: B => K)(combine: (A, B) => R): Traversable[R] = {
val keyed_a = a.map { x => (key1fn(x), x) }
val keyed_b = b.map { x => (key2fn(x), x) }
keyed_a.intersectWith(keyed_b)(combine).map(_._2)
}
/**
* Intersect two collections by their keys. Keep the ordering of
* objects in the first collection. Use a combiner function to
* combine items in common. If either item is a multi-map, then
* for a key seen `n` times in the first collection and `m`
* times in the second collection, it will occur `n * m` times
* in the resulting collection, including all the possible
* combinations of pairs of identical keys in the two collections.
*
* @param b Other collection to intersect with.
* @param keyfn Function to select the comparison key.
* @param combine Function to combine values from the two collections.
*/
def intersectBy[K, B >: A](b: Traversable[B])(keyfn: B => K)(
combine: (A, B) => B): Traversable[B] = {
val keyed_a = a.map { x => (keyfn(x), x) }
val keyed_b = b.map { x => (keyfn(x), x) }
keyed_a.intersectWith(keyed_b)(combine).map(_._2)
}
}
到目前为止,我能想出的最好的版本是:
implicit class IntersectUnionByPimp[A, Repr](a: GenTraversableLike[A, Repr]) {
def intersectBy2[K, B, R, That](b: Traversable[B])(key1fn: A => K)(
key2fn: B => K)(combine: (A, B) => R)(
implicit bf: CanBuildFrom[Repr, R, That]): That = {
// FIXME! How to make this work while calling `map`?
// val keyed_a = a.map { x => (key1fn(x), x) }
val keyed_a = mutable.Buffer[(K, A)]()
a.foreach { x => keyed_a += ((key1fn(x), x)) }
val keyed_b = b.map { x => (key2fn(x), x) }
keyed_a.intersectWith(keyed_b)(combine).map(_._2)
}
def intersectBy[K, B >: A, That](b: Traversable[B])(keyfn: B => K)(
combine: (A, B) => B)(
implicit bf: CanBuildFrom[Repr, B, That]): That = {
// FIXME! How to make this work while calling `map`?
// val keyed_a = a.map { x => (keyfn(x), x) }
val keyed_a = mutable.Buffer[(K, A)]()
a.foreach { x => keyed_a += ((keyfn(x), x)) }
val keyed_b = b.map { x => (keyfn(x), x) }
keyed_a.intersectWith(keyed_b)(combine).map(_._2)
}
首先,我不明白为什么我需要重写对map
的调用,该调用使用可变缓冲区生成keyed_a
;似乎必须有更好的方法。但我仍然在底线上得到同样的错误:
[error] /Users/benwing/devel/textgrounder/src/main/scala/opennlp/textgrounder/util/collection.scala:1018: type mismatch;
[error] found : scala.collection.mutable.Buffer[R]
[error] required: That
[error] Note: implicit method bufferToIndexedSeq is not applicable here because it comes after the application point and it lacks an explicit result type
[error] keyed_a.intersectWith(keyed_b)(combine).map(_._2)
[error] ^
所以我的问题是:
map
?intersectWith
?我知道我必须以某种方式传递CanBuildFrom
基于我收到的那个,我知道mapResult
关于构建者,但我不知道该怎么做,或者这是否可能 intersectBy
的一个示例,它与两个数字相交的浮点数列表相交,如果它们的整数部分相同,则计算绝对差值:
scala> List(4.5,2.3,4.2).intersectBy(List(4.6,4.8))(_.toInt){ case (a,b) => (a - b).abs }
res2: Traversable[Double] = List(0.09999999999999964, 0.2999999999999998, 0.39999999999999947, 0.5999999999999996)
(除了返回的类型应为List[Double]
)
感谢您的帮助。
答案 0 :(得分:1)
好的,事实证明我需要创建一个构建器来返回项目,而不是尝试直接返回它们。以下作品:
implicit class IntersectUnionByPimp[A, Repr](a: GenTraversableLike[A, Repr]) {
/**
* Intersect two collections by their keys, with separate key-selection
* functions for the two collections. This is identical to
* `intersectBy` except that each collection has its own key-selection
* function. This allows the types of the two collections to be
* distinct, with no common parent.
*
* @param b Other collection to intersect with.
* @param key1fn Function to select the comparison key for the first
* collection.
* @param key2fn Function to select the comparison key for the first
* collection.
* @param combine Function to combine values from the two collections.
*/
def intersectBy2[K, B, R, That](b: GenTraversable[B])(key1fn: A => K)(
key2fn: B => K)(combine: (A, B) => R)(
implicit bf: CanBuildFrom[Repr, R, That]): That = {
// It appears we can't call map() on `a`.
val keyed_a = mutable.Buffer[(K, A)]()
a.foreach { x => keyed_a += ((key1fn(x), x)) }
val keyed_b = b.map { x => (key2fn(x), x) }
// Nor can we return the value of map() here. Need to use a builder
// instead.
val bu = bf()
for ((_, r) <- keyed_a.intersectWith(keyed_b)(combine))
bu += r
bu.result
}
/**
* Intersect two collections by their keys. Keep the ordering of
* objects in the first collection. Use a combiner function to
* combine items in common. If either item is a multi-map, then
* for a key seen `n` times in the first collection and `m`
* times in the second collection, it will occur `n * m` times
* in the resulting collection, including all the possible
* combinations of pairs of identical keys in the two collections.
*
* @param b Other collection to intersect with.
* @param keyfn Function to select the comparison key.
* @param combine Function to combine values from the two collections.
*/
def intersectBy[K, B >: A, That](b: GenTraversable[B])(keyfn: B => K)(
combine: (A, B) => B)(
implicit bf: CanBuildFrom[Repr, B, That]): That = {
val keyed_a = mutable.Buffer[(K, A)]()
a.foreach { x => keyed_a += ((keyfn(x), x)) }
val keyed_b = b.map { x => (keyfn(x), x) }
val bu = bf()
for ((_, r) <- keyed_a.intersectWith(keyed_b)(combine))
bu += r
bu.result
}
}
我不完全确定为什么只在map
上调用GenTraversableLike
似乎不起作用,但也是如此。