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

发布于 2024-11-27 08:54:37 字数 273 浏览 2 评论 0原文

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

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

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

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

In mathematics and computer science, a tuple is an ordered list of elements. In set theory, an (ordered) n-tuple is a sequence (or ordered list) of n elements, where n is a positive integer.

So, for example, in Python the 2nd item of a tuple would be accessed via t[1].

In Scala, access is only possible via strange names t._2.

So the question is, why can't I access data in tuples as Sequence or List if it is by definition? Is there some sort of idea or just yet not inspected?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

内心旳酸楚 2024-12-04 08:54:37

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

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

Scala knows the arity of the tuples and is thus able to provide accessors like _1, _2, etc., and produce a compile-time error if you select _3 on a pair, for instance. Moreover, the type of those fields is exactly what the type used as parameter for Tuple (e.g. _3 on a Tuple3[Int, Double, Float] will return a Float).

If you want to access the nth element, you can write tuple.productElement(n), but the return type of this can only be Any, so you lose the type information.

短暂陪伴 2024-12-04 08:54:37

我相信《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)

I believe the following excerpt from "Programming in Scala: A Comprehensive Step-by-Step Guide" (Martin Odersky, Lex Spoon and Bill Venners) directly addresses both of your questions:

Accessing the elements of a tuple

You may be wondering why you can't access the elements of a tuple like
the elements of a list, for example, with "pair(0)". The reason is
that a list's apply method always returns the same type, but each
element of a tuple may be a different type: _1 can have one result
type, _2 another, and so on. These _N numbers are one-based, instead
of zero-based, because starting with 1 is a tradition set by other
languages with statically typed tuples, such as Haskell and ML.

Scala tuples get very little preferential treatment as far as the language syntax is concerned, apart from expressions '(' a1, ..., an ')' being treated by the compiler as an alias for scala.Tuplen(a1, ..., an) class instantiation. Otherwise tuples do behave as any other Scala objects, in fact they are written in Scala as case classes that range from Tuple2 to Tuple22. Tuple2 and Tuple3 are also known under the aliases of Pair and Triple respectively:

 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)
許願樹丅啲祈禱 2024-12-04 08:54:37

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

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

One big difference between List, Seq or any collection and tuple is that in tuple each element has it's own type where in List all elements have the same type.

And as consequence, in Scala you will find classes like Tuple2[T1, T2] or Tuple3[T1, T2, T3], so for each element you also have type parameter. Collections accept only 1 type parameter: List[T]. Syntax like ("Test", 123, new Date) is just syntactic sugar for Tuple3[String, Int, Date]. And _1, _2, etc. are just fields on tuple that return correspondent element.

青衫儰鉨ミ守葔 2024-12-04 08:54:37

您可以使用 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++::: 用于串联, +::+ 用于添加元素、takedropreversezip解压长度toListtoArrayto[集合],...)

You can easily achive that with 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

A lot of methods available for standard collection are also available for tuples this way (head, tail, init, last, ++ and ::: for concatenation, +: and :+ for adding elements, take, drop, reverse, zip, unzip, length, toList, toArray, to[Collection], ...)

開玄 2024-12-04 08:54:37

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

I think it's for type checking. As delnan says, if you have a tuple t and an index e (an arbitrary expression), t(e) would give the compiler no information about which element is being accessed (or even if it's a valid element for a tuple of that size). When you access elements by field name (_2 is a valid identifier, it's not special syntax), the compiler knows which field you're accessing and what type it has. Languages like Python don't really have types, so this is not necessary for them.

眼眸里的那抹悲凉 2024-12-04 08:54:37

对于正常的索引访问,可以使用任何表达式,并且在编译时检查索引表达式的结果是否保证在范围内需要花费一些精力。将其设为属性,“免费”后将出现 (1, 2)._3 的编译时错误。像只允许元组上的项目访问内的整数常量之类的事情将是一种非常特殊的情况(丑陋且不需要,有些人会说荒谬),并且需要在编译器中实现一些工作。

例如,Python 可以摆脱这个问题,因为它不会(无法)检查(即在编译时)索引是否在范围内。

With normal index access, any expression can be used, and it would take some serious effort to check at compiletime if the result of the index expression it is guaranteed to be in range. Make it an attribute, and a compile-time error for (1, 2)._3 follows "for free". Things like allowing only integer constants inside item access on tuples would be a very special case (ugly and unneeded, some would say ridiculous) and again some work to implement in the compiler.

Python, for instance, can get away with that because it wouldn't (couldn't) check (at compiletime, that is) if the index is in range anyway.

拒绝两难 2024-12-04 08:54:37

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

Apart from the benefits Jean-Philippe Pellet already mentioned this notation is also very common in mathematics (see http://en.wikipedia.org/wiki/Tuple). A lot of lecturers append indexes to tuple variables if they want to referring to the elements of a tuple. And the common (LaTeX) notation for writing "with index n" (referring to the n-th element of the tuple) is _n. So I find it actually very intuitive.

权谋诡计 2024-12-04 08:54:37

启动 Scala 3 元组 可以使用 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

Starting Scala 3 tuples can be indexed in type-safe manner using apply method which is implemented with facilities such as match types, literal-based singleton types, and dependent functions types

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

giving

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

Note the precise type which changes according to type of element at particular index. Also out of bounds index t(3) results in compile-time error

t(3)
  ^
  index out of bounds: 3
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文