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

时间:2011-07-30 16:00:03

标签: scala tuples iterable-unpacking

  

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

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

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

所以问题是,为什么我不能按照定义访问元组中的数据作为序列或列表?是否有某种想法或尚未检查过?

8 个答案:

答案 0 :(得分:90)

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

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

答案 1 :(得分:33)

我相信以下摘录自“Scala编程:全面的循序渐进指南”(Martin Odersky,Lex Spoon和Bill Venners)直接解决了您的两个问题:

  

访问元组的元素

     

您可能想知道为什么您无法访问元组的元素   列表的元素,例如,“pair(0)”。原因是   列表的apply方法总是返回相同的类型,但每个类型   元组的元素可以是不同的类型:_1可以有一个结果   type,_2 another,依此类推。这些_N数字是基于一个的   从零开始,因为从1开始是由其他人设定的传统   具有静态类型元组的语言,例如Haskell和ML。

就语言语法而言,除了表达式'(' a1, ..., an ')'被编译器视为scala.Tuplen( a1,...,an的别名)之外,Scala元组得到的优惠待遇非常少。 )类实例化。否则,元组的行为与任何其他Scala对象一样,实际上它们是用Scala编写的case classes that range from Tuple2 to 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)

答案 2 :(得分:20)

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

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

答案 3 :(得分:12)

您可以使用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

许多可用于标准收集的方法也可用于这种方式的元组(headtailinitlast++和{ {1}}用于连接,:::+:用于添加元素,:+takedropreverse,{{1} },zipunziplengthtoList,...)

答案 4 :(得分:7)

使用正常的索引访问,可以使用任何表达式,如果索引表达式的结果保证在范围内,则需要在编译时检查一些。使它成为一个属性,(1, 2)._3的编译时错误跟随“免费”。像元组中的项目访问中只允许整数常量这样的事情将是一个非常特殊的情况(丑陋和不需要,有些人会说是荒谬的)并且还有一些工作要在编译器中实现。

例如,Python可以逃脱它,因为它无法(无法)检查(在编译时,即)索引是否在范围内。

答案 5 :(得分:7)

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

答案 6 :(得分:0)

除了Jean-Philippe Pellet已经提到的好处之外,这种符号在数学中也很常见(见http://en.wikipedia.org/wiki/Tuple)。如果想要引用元组的元素,很多讲师会将索引附加到元组变量。编写“with index n ”(引用元组的 n -th元素)的通用(LaTeX)表示法是_n。所以我发现它实际上非常直观。

答案 7 :(得分:0)

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

/** 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