Почему синтаксис Scala для кортежей настолько необычен?
В математике и информатике кортеж представляет собой упорядоченный список элементов. В теории множеств (упорядоченный) n-кортеж представляет собой последовательность (или упорядоченный список) из n элементов, где n - положительное целое число.
Итак, например, в Python 2-й элемент кортежа будет доступен через t[1]
.
В Scala доступ возможен только через странные имена t._2
.
Итак, вопрос в том, почему я не могу получить доступ к данным в кортежах как к последовательности или списку, если это по определению? Есть ли какая-то идея или еще не проверена?
Ответы
Ответ 1
Scala знает арифтичность кортежей и, таким образом, может предоставлять аксессоры, такие как _1
, _2
и т.д., и создавать ошибку времени компиляции, если вы выбрали _3
для пары, например, Более того, тип этих полей является тем, что тип, используемый как параметр для Tuple
(например, _3
на Tuple3[Int, Double, Float]
, вернет a Float
).
Если вы хотите получить доступ к n-му элементу, вы можете написать tuple.productElement(n)
, но тип возвращаемого значения может быть только Any
, поэтому вы теряете информацию о типе.
Ответ 2
Я считаю, что следующий отрывок из "Программирование в Scala: всестороннее пошаговое руководство" (Мартин Одерски, Лекс Лоун и Билл Веннерс) напрямую затрагивает оба ваших вопроса:
Доступ к элементам кортежа
Возможно, вам интересно, почему вы не можете получить доступ к элементам кортежа, например элементы списка, например, с "pair (0)". Причина в том, что что метод применения списка всегда возвращает тот же тип, но каждый элемент кортежа может быть другого типа: _1 может иметь один результат тип, _2 другой и т.д. Эти числа _N являются однонаправленными, вместо этого от нуля, потому что начиная с 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)
Ответ 3
Одно большое различие между List
, Seq
или любым набором и кортежем состоит в том, что в кортеже каждый элемент имеет свой собственный тип, где в List все элементы имеют один и тот же тип.
И как следствие, в Scala вы найдете классы типа Tuple2[T1, T2]
или Tuple3[T1, T2, T3]
, поэтому для каждого элемента у вас также есть параметр типа. Коллекции принимают только один параметр типа: List[T]
. Синтаксис, подобный ("Test", 123, new Date)
, - это просто синтаксический сахар для Tuple3[String, Int, Date]
. И _1
, _2
и т.д. - это только поля на кортеже, которые возвращают соответствующий элемент.
Ответ 4
Вы можете легко достичь этого с помощью 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]
,...)
Ответ 5
При нормальном доступе к индексу любое выражение может быть использовано, и для проверки на compiletime потребуется некоторое серьезное усилие, если результат выражения индекса гарантированно находится в зоне действия. Сделайте это атрибутом, и ошибка времени компиляции для (1, 2)._3
следует за "бесплатно". Вещи, подобные разрешению только целочисленных констант внутри доступа к элементам на кортежах, были бы очень особенным случаем (уродливым и ненужным, некоторые говорили бы смешно) и снова некоторые работы для реализации в компиляторе.
Python, например, может уйти от этого, потому что он не сможет (не может) проверить (в compiletime, то есть), если индекс все равно находится в зоне.
Ответ 6
Я думаю, что для проверки типов. Как говорит Делнан, если у вас есть кортеж t
и индекс e
(произвольное выражение), t(e)
не даст компилятору никакой информации о том, к какому элементу обращаются (или даже если он является допустимым элементом для кортежа такого размера). Когда вы обращаетесь к элементам по имени поля (_2
- действительный идентификатор, это не специальный синтаксис), компилятор знает, к какому полю вы обращаетесь и какому типу он принадлежит. Языки, подобные Python, на самом деле не имеют типов, поэтому для них это не нужно.
Ответ 7
Помимо преимуществ, которые Жан-Филипп Пелле уже упомянул, эта нотация также очень распространена в математике (см. http://en.wikipedia.org/wiki/Tuple). Многие лекторы добавляют индексы к кортевым переменным, если они хотят ссылаться на элементы кортежа. И общая (LaTeX) запись для записи "с индексом n" (относящаяся к n-му элементу кортежа) равна _n
. Поэтому я нахожу это на самом деле очень интуитивным.