当映射到相同类型时,Scala 的 map() 的行为是否应该不同?

2023-12-13

在Scala Collections框架中,我认为使用时存在一些违反直觉的行为map().

我们可以区分(不可变)集合的两种转换。那些其实现调用newBuilder重新创建结果集合,以及那些经历隐式CanBuildFrom获得建造者。

第一类包含所有转换,其中所包含元素的类型不会改变。例如,他们是filter, partition, drop, take, span等等。这些转换可以自由调用newBuilder并重新创建与调用它们的集合类型相同的集合类型,无论多么具体:过滤List[Int]总是可以返回一个List[Int];过滤一个BitSet(或者RNA中描述的示例结构本文介绍集合框架的架构) 总是可以返回另一个BitSet (or RNA)。让我们称他们为过滤变换.

第二类转型需要CanBuildFrom为了更加灵活,因为所包含元素的类型可能会发生变化,因此,集合本身的类型可能无法重用:BitSet不能包含Strings; an RNA仅包含Bases。这种转变的例子是map, flatMap, collect, scanLeft, ++等等。我们称它们为映射变换.

现在这是要讨论的主要问题。无论集合的静态类型是什么,所有过滤转换都会返回相同的集合类型,而映射操作返回的集合类型可能会根据静态类型而有所不同。

scala> import collection.immutable.TreeSet
import collection.immutable.TreeSet

scala> val treeset = TreeSet(1,2,3,4,5) // static type == dynamic type
treeset: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 2, 3, 4, 5)

scala> val set: Set[Int] = TreeSet(1,2,3,4,5) // static type != dynamic type
set: Set[Int] = TreeSet(1, 2, 3, 4, 5)

scala> treeset.filter(_ % 2 == 0)
res0: scala.collection.immutable.TreeSet[Int] = TreeSet(2, 4) // fine, a TreeSet again

scala> set.filter(_ % 2 == 0)    
res1: scala.collection.immutable.Set[Int] = TreeSet(2, 4) // fine

scala> treeset.map(_ + 1)        
res2: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 3, 4, 5, 6) // still fine

scala> set.map(_ + 1)    
res3: scala.collection.immutable.Set[Int] = Set(4, 5, 6, 2, 3) // uh?!

现在,我明白为什么会这样了。已解释there and there。简而言之:隐含的CanBuildFrom是基于静态类型插入的,并且取决于其实现def apply(from: Coll)方法,可能能够也可能不能重新创建相同的集合类型。

现在我唯一的观点是,当我们知道我们正在使用映射操作生成一个集合时相同的元素类型(编译器可以静态确定),我们可以模仿过滤转换的工作方式并使用集合的本机构建器。我们可以重复使用BitSet当映射到Ints,创建一个新的TreeSet具有相同的顺序,等等。

那么我们就可以避免这样的情况

for (i <- set) {
  val x = i + 1
  println(x)
}

不打印增加的元素TreeSet按照相同的顺序

for (i <- set; x = i + 1)
  println(x)

So:

  • 您认为改变所描述的映射转换的行为是个好主意吗?
  • 我严重忽视了哪些不可避免的警告?
  • 如何实施?

我在想类似的事情implicit sameTypeEvidence: A =:= B参数,可能有默认值null(或者更确切地说implicit canReuseCalleeBuilderEvidence: B <:< A = null),可以在运行时使用它来向CanBuildFrom,这又可用于确定要返回的构建器的类型。


我又看了一遍,我认为你的问题并不是由 Scala 集合的特定缺陷引起的,而是缺少 的构建器TreeSet。因为以下内容确实按预期工作:

val list = List(1,2,3,4,5)
val seq1: Seq[Int] = list
seq1.map( _ + 1 ) // yields List

val vector = Vector(1,2,3,4,5)
val seq2: Seq[Int] = vector
seq2.map( _ + 1 ) // yields Vector

所以原因是TreeSet缺少专门的伴随对象/构建器:

seq1.companion.newBuilder[Int]    // ListBuffer
seq2.companion.newBuilder[Int]    // VectorBuilder
treeset.companion.newBuilder[Int] // Set (oops!)

所以我的猜测是,如果你为你的这样一个伴侣做好了适当的准备RNA类,你可能会发现两者map and filter如你所愿工作...?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当映射到相同类型时,Scala 的 map() 的行为是否应该不同? 的相关文章

随机推荐

  • 固定大小的 HashMap 的最佳容量和负载因子是多少?

    我正在尝试找出特定情况下的最佳容量和负载系数 我想我已经明白了它的要点 但我仍然感谢比我更有知识的人的确认 如果我知道我的 HashMap 将填满以包含 100 个对象 并且大部分时间都会包含 100 个对象 那么我猜测最佳值是初始容量 1
  • 在 RowMapper 中使用查询

    在java中我会做类似下面的事情来迭代resultset并形成查询 public Map
  • 是否可以创建一个非居中的 Javascript 警报框?

    对于标准 Javascriptalert 盒子 是否可以让它出现在屏幕中心以外的位置 您能指定水平和 或垂直位置吗 Afaik 它无法放置在您想要的位置 您始终可以使用在功能和自定义方面提供更多功能的 JS 框架 工具包解决方案之一 而不是
  • pandas 替换一列中的多个值

    在风险级别列中 我想将 小 替换为 1 将 中 替换为 5 将 高 替换为 15 我试过 dfm replace risk Small 1 risk Medium 5 risk High 15 但仅更换了介质 怎么了 您的替换格式已关闭 I
  • 基于分组返回列的 SQL 查询

    我的选择如下 SELECT SUBSTRING Col1 1 3 AS Series b Col2 CAST c Price c Qty AS MONEY AS Total FROM tableName a inner join WHERE
  • sitecore 中的基本 MembershipProvider

    我正在尝试为 sitecore 实现一个非常非常简单的 MembershipProvider 但我不确定它是否是too简单到实际工作 基本上我们已经有一个用于用户数据的自定义存储 所以我知道客户MembershipProvider是要走的路
  • 删除字符串中某个位置的字符 - javascript [重复]

    这个问题在这里已经有答案了 有没有一种简单的方法可以删除javascript中某个位置的字符 例如如果我有字符串 Hello World 我可以删除位置3处的字符吗 我想要的结果如下 Helo World 这个问题不是重复的如何使用 Jav
  • 无法构建 apk,firebase_admob 出现错误

    我正在尝试构建 apk 但如果我没有运行 firebase admob 就会出现问题firebase admob那么 apk 可以完美运行并且构建没有任何错误 如何解决该问题 我收到此错误 在这种情况下 任务 firebase admob
  • 石头剪刀布程序不工作(Python)

    问题 程序似乎不接受输入的整数 不会增加赢 输 平局计数 并且不会在调试模式下显示计算机选择 程序的基本设计 编写一个程序 让用户与计算机玩石头 剪刀 布游戏 该程序应按如下方式工作 将显示一个菜单 战绩 0胜0平0负 调试显示计算机的选择
  • Eclipse RCP:如何排序属于不同插件的透视按钮?

    我的应用程序有 5 个插件 每个插件都有自己的视角 因此每个视角扩展定义都位于各个插件的plugin xml 下 现在 我想控制这些视角在我的应用程序中出现的顺序 怎么做 有一个主要插件包含 ApplicationWorkBenchAdvi
  • 如何更改工具栏主页图标颜色

    我正在使用 android support v7 widget Toolbar 并从中学习这个帖子如何将汉堡包图标的颜色更改为白色 但当我打电话时向上 后箭头仍然是深色 setDisplayHomeAsUpEnabled true 我怎样才
  • 如何在 Java 6 中使用为 Java 7 编译的库?

    我正在开发一个 Java 应用程序 我需要使用一个名为myBeans jar This myBeans jar包含许多类文件 这些文件是用jdk 1 7 我没有这些类文件的源代码 我的整个应用程序正在使用jdk 1 6 我无法将其编译器更改
  • SLComposeViewController 有屏幕截图但没有图像附件

    我注意到 Mobile Safari 的 Twitter 和 Facebook 共享添加了当前页面的屏幕截图 但并未实际共享它 例如 我试图通过 SLComposeViewController 复制此内容 但是调用添加图片 实际上将 UII
  • 如何限制 Open JDK 11 上的非堆大小

    我们正在 AWS 中运行基于 openjdk 11 slim 的 docker 容器 我们通常使用两种实例类型 一个有 1G 另一个有 2G 可用内存 在 1G 实例上 我开始使用这些参数来限制堆大小 XX MaxRAM 1g XX Max
  • Angular2 - 将 ngFor item 作为参数传递到管道中?

    我尝试将 ngFor 项目作为参数传递到管道中 但收到错误 异常 调用节点模块失败并出现错误 错误 模板 解析错误 TypeError 无法读取属性 toUpperCase undefined name ng container 错误 gt
  • 生成自定义字母数字序列

    我正在尝试生成自定义字母数字序列 顺序如下 AA0 AA9 AB0 AB9 AC0 AC9 等等 简而言之 有3个地方需要填补 On the first place the values can go from A to Z On the
  • Python如何延迟子弹射击

    预先感谢您抽出时间 如果我用空格键发射子弹 则会创建一串子弹 如果我在 if keys pygame K SPACE 中使用 time sleep 设置延迟 它也会将循环冻结设定的秒数 我怎样才能做到一次只发射一颗子弹 这是我的游戏循环的副
  • 以另一个用户身份运行 jenkins 作业

    我使用指南安装了 jenkins 该指南在服务器中创建了一个 jenkins 用户 并且显然在其下运行了 jenkins 服务器 我在服务器上的所有设置 虚拟环境 python 包安装 都是针对不同的用户 ci user 的 有什么办法让我
  • 什么是 WPF 预览事件?

    我一直在寻找事件 Preview 的描述 就像每个元素都有事件 KeyDown 和 PreviewKeyDown 一样 有什么区别 不是一个是附加事件一个不是 真正的约定的区别和编程方式的区别 在任何从 Control 派生的类中 您都可以
  • 当映射到相同类型时,Scala 的 map() 的行为是否应该不同?

    在Scala Collections框架中 我认为使用时存在一些违反直觉的行为map 我们可以区分 不可变 集合的两种转换 那些其实现调用newBuilder重新创建结果集合 以及那些经历隐式CanBuildFrom获得建造者 第一类包含所