在数学和计算机科学中,元组是元素的有序列表。在集合论中,一个(有序的)n 元组是一个包含 n 个元素的序列(或有序列表),其中 n 是一个正整数。
因此,例如,在 Python 中,元组的第二项将通过t[1]
.
在 Scala 中,只能通过奇怪的名称进行访问t._2
。
所以问题是,如果按照定义,为什么我不能以序列或列表的形式访问元组中的数据?是否有某种想法或尚未检查?
在数学和计算机科学中,元组是元素的有序列表。在集合论中,一个(有序的)n 元组是一个包含 n 个元素的序列(或有序列表),其中 n 是一个正整数。
因此,例如,在 Python 中,元组的第二项将通过t[1]
.
在 Scala 中,只能通过奇怪的名称进行访问t._2
。
所以问题是,如果按照定义,为什么我不能以序列或列表的形式访问元组中的数据?是否有某种想法或尚未检查?
Scala 知道元组的数量,因此能够提供诸如_1
,等的访问器,并且如果您在一对上进行_2
选择,则会产生编译时错误。_3
此外,这些字段的类型正是用作参数的类型Tuple
(例如_3
,在 aTuple3[Int, Double, Float]
上将返回 a Float
)。
如果要访问第 n 个元素,可以写tuple.productElement(n)
,但是 this 的返回类型只能是Any
,所以会丢失类型信息。
我相信以下摘自“Scala 编程:全面的分步指南”(Martin Odersky、Lex Spoon 和 Bill Venners)直接解决了您的两个问题:
访问元组的元素
您可能想知道为什么不能像访问列表元素一样访问元组的元素,例如使用“pair(0)”。原因是列表的 apply 方法总是返回相同的类型,但元组的每个元素可能是不同的类型:_1 可以有一种结果类型,_2 可以有另一种,依此类推。这些 _N 数字是从 1 开始的,而不是从 0 开始的,因为从 1 开始是其他具有静态类型元组的语言设置的传统,例如 Haskell 和 ML。
就语言语法而言,Scala 元组很少得到优先处理,除了'(' a1, ..., an ')'
编译器将表达式视为 scala.Tuplen( a1, ..., an ) 类实例化的别名。否则,元组的行为与任何其他 Scala 对象一样,实际上它们是在 Scala 中作为案例类编写的,范围从 Tuple2 到 Tuple22。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)
或任何集合和元组之间的一个很大区别是List
,Seq
在元组中,每个元素都有自己的类型,而在 List 中,所有元素都具有相同的类型。
因此,在 Scala 中,您会找到类似Tuple2[T1, T2]
or的类Tuple3[T1, T2, T3]
,因此对于每个元素,您也有类型参数。集合仅接受 1 个类型参数:List[T]
. 语法 like("Test", 123, new Date)
只是Tuple3[String, Int, Date]
. 和_1
,_2
等只是返回对应元素的元组上的字段。
您可以使用 shapeless 轻松实现:
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
许多可用于标准集合的方法也可用于元组(head
, tail
, init
, last
,++
和:::
用于连接,+:
以及:+
添加元素,take
, drop
, reverse
, zip
, unzip
, length
, toList
, toArray
, to[Collection]
, ...)
我认为这是用于类型检查。正如 delnan 所说,如果您有一个元组t
和一个索引e
(任意表达式),t(e)
则不会向编译器提供有关正在访问哪个元素的信息(或者即使它是该大小的元组的有效元素)。当您按字段名称访问元素时(_2
是一个有效的标识符,它不是特殊语法),编译器知道您正在访问哪个字段以及它具有什么类型。像 Python 这样的语言并没有真正的类型,所以这对它们来说不是必需的。
通过正常的索引访问,可以使用任何表达式,并且在编译时检查索引表达式的结果是否保证在范围内需要花费一些精力。使其成为一个属性,并在(1, 2)._3
“免费”之后出现编译时错误。像只允许在元组的项目访问中使用整数常量之类的事情将是一种非常特殊的情况(丑陋且不需要,有些人会说荒谬),并且需要在编译器中实现一些工作。
例如,Python 可以避免这种情况,因为它不会(不能)检查(即在编译时)索引是否在范围内。
除了 Jean-Philippe Pellet 已经提到的好处之外,这种符号在数学中也很常见(参见http://en.wikipedia.org/wiki/Tuple)。如果他们想要引用元组的元素,许多讲师会将索引附加到元组变量。用于编写“索引为n ”(指元组的第n个元素)的常用(LaTeX)表示法是_n
. 所以我发现它实际上非常直观。
可以使用apply方法以类型安全的方式对启动 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