如何确保在map期间保留自定义Scala集合的动态类型?

如何解决如何确保在map期间保留自定义Scala集合的动态类型?

| 我阅读了有关Scala 2.8集合的体系结构的非常有趣的文章,并且我已经在进行一些试验。首先,我只复制了漂亮的
RNA
示例的最终代码。这里供参考:
abstract class Base
case object A extends Base
case object T extends Base
case object G extends Base
case object U extends Base

object Base {
  val fromInt: Int => Base = Array(A,T,G,U)
  val toInt: Base => Int = Map(A -> 0,T -> 1,G -> 2,U -> 3)
}

final class RNA private (val groups: Array[Int],val length: Int)
    extends IndexedSeq[Base] with IndexedSeqLike[Base,RNA] {

  import RNA._

  // Mandatory re-implementation of `newBuilder` in `IndexedSeq`
  override protected[this] def newBuilder: Builder[Base,RNA] =
    RNA.newBuilder

  // Mandatory implementation of `apply` in `IndexedSeq`
  def apply(idx: Int): Base = {
    if (idx < 0 || length <= idx)
      throw new IndexOutOfBoundsException
    Base.fromInt(groups(idx / N) >> (idx % N * S) & M)
  }

  // Optional re-implementation of foreach,// to make it more efficient.
  override def foreach[U](f: Base => U): Unit = {
    var i = 0
    var b = 0
    while (i < length) {
      b = if (i % N == 0) groups(i / N) else b >>> S
      f(Base.fromInt(b & M))
      i += 1
    }
  }
}

object RNA {

  private val S = 2 // number of bits in group
  private val M = (1 << S) - 1 // bitmask to isolate a group
  private val N = 32 / S // number of groups in an Int

  def fromSeq(buf: Seq[Base]): RNA = {
    val groups = new Array[Int]((buf.length + N - 1) / N)
    for (i <- 0 until buf.length)
      groups(i / N) |= Base.toInt(buf(i)) << (i % N * S)
    new RNA(groups,buf.length)
  }

  def apply(bases: Base*) = fromSeq(bases)

  def newBuilder: Builder[Base,RNA] =
    new ArrayBuffer mapResult fromSeq

  implicit def canBuildFrom: CanBuildFrom[RNA,Base,RNA] =
    new CanBuildFrom[RNA,RNA] {
      def apply(): Builder[Base,RNA] = newBuilder
      def apply(from: RNA): Builder[Base,RNA] = newBuilder
    }
}
现在,这是我的问题。如果运行此命令,一切正常:
val rna = RNA(A,U)
println(rna.map(e => e)) // prints RNA(A,U)
但是这段代码将RNA转换为Vector!
val rna: IndexedSeq[Base] = RNA(A,U)
println(rna.map(e => e)) // prints Vector(A,U)
这是一个问题,因为不知道
RNA
类的客户端代码可能会将其转换回
Vector
,而不是仅从
Base
映射到
Base
时。为什么会这样,有什么解决方法? 附言:我找到了一个初步的答案(见下文),如果我错了,请纠正我。     

解决方法

如果
rna
变量的静态类型为
IndexedSeq[Base]
,则自动插入的
CanBuildFrom
不能是
RNA
伴随对象中定义的那个,因为编译器不应该知道
rna
RNA
的实例。 那么它是从哪里来的呢?编译器将回退到
GenericCanBuildFrom
的实例,该实例在
IndexedSeq
对象中定义。
GenericCanBuildFrom
通过在原始集合上调用
genericBuilder[B]
来生成其生成器,并且对该通用生成器的要求是它可以生成可以容纳任何类型
B
的泛型集合-当然,传递给
map()
的函数的返回类型不受限制。 在这种情况下,
RNA
只是
IndexedSeq[Base]
,而不是通用的
IndexedSeq
,因此不可能覆盖
RNA
中的
genericBuilder[B]
以返回特定于
RNA
的构建器—我们将不得不在运行时检查
B
Base
还是其他东西,但是我们不能那样做。 我认为这可以解释为什么我们要退还5英镑。至于我们如何解决它,这是一个悬而未决的问题…… 编辑:修复此问题需要
map()
知道它是否映射到
A
的子类型。为此,需要对馆藏库进行重大更改。查看相关问题Scala的map()在映射到相同类型时是否应该表现出不同?     ,关于为什么我认为静态键入比RNA弱的类型不是一个好主意。它实际上应该是一条评论(因为它更像是一条意见,但很难阅读)。从您的评论到我的评论:   为什么不?作为IndexedSeq [Base]的子类,根据Liskov替换原则,RNA能够完成IndexedSeq [Base]的所有工作。有时,您所知道的只是它是一个IndexedSeq,您仍然希望过滤器,地图和好友保持相同的特定实现。实际上,过滤器可以做到-但不是地图
filter
这样做是因为编译器可以静态地保证它。如果保留特定集合中的元素,则最终会得到相同类型的集合。
map
不能保证,这取决于传递的功能。 我的意思更多是关于明确指定类型并期望超出其传递范围的行为。作为RNA收集的用户,我可能编写取决于该收集的某些属性(例如有效的内存表示形式)的代码。 因此,假设我在ѭ33中声明
rna
只是an15ѭ。几行之后,我调用了方法“ 36”,该方法期望有效的内存表示形式,对此最好的签名是什么?
def doSomething[T](rna: IndexedSeq[Base]): T
还是
def doSomething[T](rna: RNA): T
? 我认为应该是后者。但是如果真是这样,那么代码将不会编译,因为
rna
并不是静态的
RNA
对象。如果方法签名应该是前者,那么实质上我是说我不在乎内存表示效率。因此,我认为明确指定弱类型但期望强行为的行为是矛盾的。您在示例中所做的是。 现在我确实看到了,即使我这样做了:
val rna = RNA(A,G,T,U)
val rna2 = doSomething(rna)
别人写的地方:
def doSomething[U](seq: IndexedSeq[U]) = seq.map(identity)
我想让
rna2
成为
RNA
对象,但是那不会发生...这意味着如果希望调用者获得更具体的类型,那么其他人应该编写一个采用
CanBuildFrom
的方法:
def doSomething[U,To](seq: IndexedSeq[U])
   (implicit cbf: CanBuildFrom[IndexedSeq[U],U,To]) = seq.map(identity)(cbf)
那我可以打电话给:
val rna2: RNA = doSomething(rna)(collection.breakOut)
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-