为什么 Scala 的元组语法如此不同寻常?

在数学和计算机科学中,元组是元素的有序列表。在集合论中,(有序) n 元组是 n 个元素的序列(或有序列表) ,其中 n 是正整数。

因此,例如,在 Python 中,元组的第二个条目将通过 t[1]访问。

在 Scala 中,只能通过奇怪的名称 t._2进行访问。

所以问题是,为什么不能以序列或列表的形式访问元组中的数据呢?是有什么想法,还是还没有检查?

30249 次浏览

通过正常的索引访问,可以使用任何表达式,而且如果索引表达式的结果保证在范围内,那么在编译时将需要花费很大的努力来检查。如果将其设置为属性,则 (1, 2)._3的编译时错误将在“ for free”之后出现。在元组的条目访问中只允许整数常量是非常特殊的情况(丑陋且不必要,有些人会说荒谬) ,而且还需要在编译器中实现一些工作。

例如,Python 可以不受惩罚,因为它不会(不能)检查(在编译时)索引是否在范围内。

Scala 知道元组的特性,因此能够提供诸如 _1_2等等的访问器,并且如果在一个对上选择 _3,就会产生编译时错误。此外,这些字段的类型正是用作 Tuple参数的类型(例如,Tuple3[Int, Double, Float]上的 _3将返回 Float)。

如果要访问第 n 个元素,可以编写 tuple.productElement(n),但是返回类型只能是 Any,因此会丢失类型信息。

我想是用来检查类型的。正如 delnan 所说,如果您有一个元组 t和一个索引 e(一个任意的表达式) ,t(e)将不会给编译器关于正在访问哪个元素的信息(或者即使它是那种大小的元组的有效元素)。当您通过字段名访问元素时(_2是一个有效的标识符,它不是特殊的语法) ,编译器知道您访问的是哪个字段以及它的类型。像 Python 这样的语言实际上没有类型,所以对它们来说没有必要这样做。

ListSeq或任何集合和元组之间的一个很大区别是,在元组中,每个元素都有自己的类型,而在 List 中,所有元素都有相同的类型。

因此,在 Scala 中可以找到类似于 Tuple2[T1, T2]Tuple3[T1, T2, T3]的类,因此对于每个元素,都有类型参数。集合只接受1个类型参数: List[T]。像 ("Test", 123, new Date)这样的语法只是 Tuple3[String, Int, Date]的语法糖。而 _1_2等等只是元组上返回对应元素的字段。

我相信以下摘录自《 Scala 编程: 一个全面的步骤指南》(Martin Odersky,Lex Spoon and Bill Venners) ,它直接回答了你的两个问题:

访问元组的元素

您可能想知道为什么不能访问元组的元素 例如,一个列表的元素使用“成对(0)”。原因是 列表的 application 方法总是返回相同的类型,但是每个 元组的元素可以是不同的类型: _ 1可以有一个结果 类型,_ 2另一个,以此类推。这些 _ N 数字是以一为基础的 从零开始,因为从1开始是由其他 具有静态类型元组的语言,如 Haskell 和 ML。

就语言语法而言,Scala 元组得到的优先待遇非常少,除了编译器将表达式 '(' a1, ..., an ')'作为 Scala 的别名。Tuplen (A1,... ,an)类实例化。否则元组的行为就像任何其他 Scala 对象一样,实际上它们是以 从 Tuple2到 Tuple22的 case 类的形式在 Scala 中编写的。Tuple2和 Tuple3也分别以 Pair 和 Triple 的别名为人所知:

 val a = Pair   (1,"two")      // same as Tuple2 (1,"two") or (1,"two")
val b = Triple (1,"two",3.0)  // same as Tuple3 (1,"two",3.0) or (1,"two",3.0)

你可以很容易地通过 没有形状实现这一点:

import shapeless.syntax.std.tuple._


val t = ("a", 2, true, 0.0)


val s = t(0) // String at compile time
val i = t(1) // Int at compile time
// etc

许多可用于标准采集的方法也可用于元组(headtailinitlast++:::用于串联,+::+用于添加元素,takedroptail0,tail1,tail2,tail3,tail4,tail5,tail6,...)

除了 Jean-Philippe Pellet 已经提到的好处之外,这个符号在数学中也是非常普遍的(参见 http://en.wikipedia.org/wiki/Tuple)。许多讲师如果想要引用元组的元素,就会在元组变量中附加索引。用于编写“ with index N”(指元组的 N-th 元素)的通用(LaTeX)表示法是 _n。所以我觉得这很直观。

启动 Scala 3 元组可以使用 申请方法以类型安全的方式进行索引,申请方法使用匹配类型、基于文本的单例类型和依赖函数类型等工具实现

/** Get the i-th element of this tuple.
*  Equivalent to productElement but with a precise return type.
*/
inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] =
runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]]

给予

val t = (42, 3.14, "woohoo")
// t: Tuple3[Int, Double, String] = (42,3.14,woohoo)
t(0)
// res0: Int = 42
t(1)
// res1: Double = 3.14
t(2)
// res2: String = woohoo

请注意根据特定索引处的元素类型变化的精确类型。另外,超出界限索引 t(3)会导致编译时错误

t(3)
^
index out of bounds: 3