加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 避免泛型类中的向下倾斜

发布时间:2020-12-16 10:05:14 所属栏目:安全 来源:网络整理
导读:注意到我的代码实际上是在列表上迭代并更新Map中的值,我首先创建了一个简单的辅助方法,该方法采用了一个函数来转换map值并返回一个更新的map.随着程序的发展,它获得了一些其他Map转换函数,因此很自然地将它转换为一个隐式值类,它将方法添加到 scala.collecti
注意到我的代码实际上是在列表上迭代并更新Map中的值,我首先创建了一个简单的辅助方法,该方法采用了一个函数来转换map值并返回一个更新的map.随着程序的发展,它获得了一些其他Map转换函数,因此很自然地将它转换为一个隐式值类,它将方法添加到 scala.collection.immutable.Map [A??,B].该版本运行正常.

但是,没有任何关于需要特定地图实现的方法,它们似乎适用于scala.collection.Map [A??,B]甚至是MapLike.所以我希望它在地图类型以及键和值类型中是通用的.这就是梨形的地方.

我当前的迭代看起来像这样:

implicit class RichMap[A,B,MapType[A,B] <: collection.Map[A,B]](
    val self: MapType[A,B]
) extends AnyVal {
  def updatedWith(k: A,f: B => B): MapType[A,B] =
    self updated (k,f(self(k)))
}

此代码无法编译,因为自更新(k,f(self(k)))是scala.collection.Map [A??,B],它不是MapType [A,B].换句话说,self.updated的返回类型就好像self的类型是上层类型而不是实际声明的类型.

我可以通过向下转换“修复”代码:

def updatedWith(k: A,B] =
  self.updated(k,f(self(k))).asInstanceOf[MapType[A,B]]

这种感觉并不令人满意,因为向下转换是一种代码气味并且表明对类型系统的误用.在这种特殊情况下,似乎值始终是强制转换类型,并且整个程序编译并正确运行此向下转换支持此视图,但它仍然有气味.

那么,是否有更好的方法来编写此代码以使scalac在不使用向下转换的情况下正确推断类型,或者这是编译器限制并且需要向下转换?

[已编辑添加以下内容.]

我使用这种方法的代码有点复杂和混乱,因为我仍在探索一些想法,但最小的例子是将频率分布计算为副作用,代码大致如下:

var counts = Map.empty[Int,Int] withDefaultValue 0
for (item <- items) {
  // loads of other gnarly item-processing code
  counts = counts updatedWith (count,1 + _)
}

在撰写本文时,我的问题有三个答案.

一个归结为只是让updatedWith返回一个scala.collection.Map [A??,B].从本质上讲,它接受并返回了一个immutable.Map [A??,并使该类型不那么具体.换句话说,它仍然不够通用,并为调用者使用的类型设置策略.我当然可以更改计数声明中的类型,但这也是一个代码气味来解决返回错误类型的库,而它真正做的就是将向下转换为调用者的代码.所以我根本不喜欢这个答案.

另外两个是CanBuildFrom和构建器的变体,因为它们基本上遍历地图以生成修改后的副本.一个内联修改的更新方法,而另一个调用原始更新并将其附加到构建器,因此似乎进行了额外的临时副本.两者都是解决类型正确性问题的好答案,尽管从性能角度来看,避免额外复制的问题更好,我更喜欢这个问题.然而另一个更短,可以说更清楚地表明意图.

在假设的不可变映射的情况下,它以与List类似的方式共享大树,这种复制会破坏共享并降低性能,因此最好使用现有的修改而不执行复制.但是,Scala的不可变映射似乎并没有这样做,因此复制(一次)似乎是一种实用的解决方案,不太可能在实践中产生任何影响.

解决方法

是!使用 CanBuildFrom.这是Scala集合库使用CanBuildFrom证据推断最接近的集合类型的方式.只要您有CanBuildFrom [From,Elem,To]的隐式证据,其中From是您开始使用的集合类型,Elem是集合中包含的类型,To是您想要的最终结果. CanBuildFrom将提供一个可以向其添加元素的Builder,当您完成后,您可以调用Builder#result()来获取相应类型的完整集合.

在这种情况下:

From = MapType[A,B]
Elem = (A,B) // The type actually contained in maps
To = MapType[A,B]

执行:

import scala.collection.generic.CanBuildFrom

implicit class RichMap[A,f: B => B)(implicit cbf: CanBuildFrom[MapType[A,(A,B),B]]): MapType[A,B] = {
    val builder = cbf()
    builder ++= self.updated(k,f(self(k)))
    builder.result()
  }
}

scala> val m = collection.concurrent.TrieMap(1 -> 2,5 -> 3)
m: scala.collection.concurrent.TrieMap[Int,Int] = TrieMap(1 -> 2,5 -> 3)

scala> m.updatedWith(1,_ + 10)
res1: scala.collection.concurrent.TrieMap[Int,Int] = TrieMap(1 -> 12,5 -> 3)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读