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

yur*_*ura 70 scala tuples iterable-unpacking

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

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

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

所以问题是,为什么我不能按照定义访问元组中的数据作为序列或列表?有什么想法或者还没有检查过吗?

Jea*_*let 90

斯卡拉知道元组的元数,因此能够提供像访问器_1,_2等等,如果你选择产生编译时错误_3的一对,例如.而且,这些字段的类型正是用作参数的类型Tuple(例如_3,Tuple3[Int, Double, Float]将返回a Float).

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

  • 那为什么不支持apply()方法呢?List,Array和所有其他类型都支持它.似乎很奇怪Tuple*不支持apply方法(这会使编写tupl(0),tupl(3)等更容易...).恕我直言,这会使其与语言中的其他类型更加统一. (3认同)

Vla*_*dim 33

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

访问元组的元素

您可能想知道为什么您不能像列表元素那样访问元组的元素,例如,使用"pair(0)".原因是列表的apply方法总是返回相同的类型,但元组的每个元素可能是不同的类型:_1可以有一个结果类型,_2可以有另一个,依此类推.这些_N数字是基于1的,而不是从零开始,因为从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)
Run Code Online (Sandbox Code Playgroud)

  • 现在不推荐使用Pair和Triple.它们在Scala 2.11中被弃用 (4认同)

ten*_*shi 20

之间的一个很大的区别List,Seq任何收集和元组或者是在元组的每个元素都有它自己的类型,其中在列表中的所有元素都具有相同的类型.

而作为结果,在Scala中,你会发现类,如Tuple2[T1, T2]Tuple3[T1, T2, T3],所以对于每一个元素,你也有类型参数.集合只接受1个类型参数:List[T].语法就像("Test", 123, new Date)只是语法糖Tuple3[String, Int, Date].而且_1,_2等等只是返回对应元素的元组上的字段.


Ale*_*ult 12

您可以轻松地实现无形:

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
Run Code Online (Sandbox Code Playgroud)

很多可用于标准的收集方法也可用于元组这样(head,tail,init,last,++:::进行连结,+::+添加元素take,drop,reverse,zip,unzip,length,toList,toArray,to[Collection],...)

  • 可能的缺点是编译时间可能更长,因为这涉及"类型级"编程,即通过类型系统表示的计算和由scalac完成和完成的计算.另外一个优点和缺点的特性是,当你不能编译时调用这些方法(例如在上面的例子中调用t.take(5)),但是你通常会得到一个奇怪的错误消息scalac而不是一个明确的解释.尽管如此,一点点无形的工作可能会使最后一点变得更好 (6认同)

小智 7

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

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


Jay*_*rod 7

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