Почему синтаксис Scala для кортежей настолько необычен?


в математике и информатике Кортеж-это упорядоченный список элементов. В теории множеств (упорядоченный) N-Кортеж - это последовательность (или упорядоченный список) из n элементов, где n-положительное целое число.

Так, например, в Python 2-й элемент кортежа будет осуществляться через t[1].

в Scala, доступ возможен только через странные имена t._2.

Итак, вопрос в том, почему я не могу получить доступ к данным в кортежах как Последовательность или список, если это по определению? Есть какая-то идея или просто еще не досмотрели?

7 64

7 ответов:

Scala знает arity кортежей и, таким образом, может предоставить аксессоры, такие как _1,_2 и т. д., и создать ошибку времени компиляции, если вы выберете _3 на пару, например. Более того, тип этих полей-это именно то, что тип используется в качестве параметра для Tuple (например,_3 на Tuple3[Int, Double, Float] вернет a Float).

если вы хотите получить доступ к N-му элементу, вы можете написать tuple.productElement(n), но возвращаемый тип может быть только Any, так что вы теряете тип информация.

Я считаю, что следующий отрывок из "программирование в Scala: всестороннее Пошаговое Руководство" (Мартин Одерский, Лекс Спун и Билл Веннерс) непосредственно затрагивает оба ваших вопроса:

доступ к элементам кортежа!--7-->

Вы можете быть удивлены, почему вы не могу получить доступ к элементам кортежа, как элементы списка, например, с "pair (0)". Причина что метод apply списка всегда возвращает один и тот же тип, но каждый элемент кортежа может быть другого типа: _1 может иметь один результат типа, _2 другой, и так далее. Вместо этого эти _n числа основаны на одном на основе нуля, потому что начиная с 1-это традиция, установленная другими языки со статически типизированными кортежами, такие как Haskell и ML.

кортежи Scala получают очень мало предпочтений в отношении синтаксиса языка, кроме выражений '(' a1, ..., an ')' обрабатывается компилятором как псевдоним скала.Tuplen (a1, ..., Ан) при создании экземпляра класса. В противном случае кортежи ведут себя как любые другие объекты Scala, фактически они записываются в Scala как case классы, которые варьируются от 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 или любая коллекция и Кортеж - это то, что в кортеже каждый элемент имеет свой собственный тип, где в списке все элементы имеют один и тот же тип.

и как следствие, в Scala вы найдете такие классы, как Tuple2[T1, T2] или Tuple3[T1, T2, T3], Так что для каждого элемента у вас также есть параметр типа. Коллекции принимают только 1 параметр типа:List[T]. Синтаксис как ("Test", 123, new Date) - это просто синтаксический сахар для Tuple3[String, Int, Date]. И _1,_2 и т. д. это просто поля на кортеже, что вернуть соответствующий элемент.

вы можете легко достичь этого с бесформенные:

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], ...)

при обычном доступе к индексу можно использовать любое выражение, и потребуется некоторое серьезное усилие, чтобы проверить в compiletime, если результат выражения индекса гарантированно будет в диапазоне. Сделайте его атрибутом и ошибкой времени компиляции для (1, 2)._3 образом "бесплатно". Такие вещи, как разрешение только целочисленных констант внутри доступа к элементам на кортежах, были бы очень особым случаем (уродливым и ненужным, некоторые сказали бы смешным), и снова некоторые работы по реализации в компилятор.

Python, например, может уйти с этим, потому что он не будет (не мог) проверять (в compiletime, то есть), если индекс все равно находится в диапазоне.

Я думаю, что это для проверки типа. Как delnan говорит, Если у вас есть кортеж t индекс e (произвольное выражение), t(e) не даст компилятору никакой информации о том, к какому элементу осуществляется доступ (или даже если это допустимый элемент для кортежа такого размера). При доступе к элементам по имени Поля (_2 является допустимым идентификатором, это не специальный синтаксис), компилятор знает, к какому полю вы обращаетесь и какой тип он имеет. Такие языки, как Python, на самом деле не имеют типов, так что это им не нужно.

помимо уже упомянутых преимуществ Жан-Филипп Пелле эта нотация также очень распространена в математике (см. http://en.wikipedia.org/wiki/Tuple). многие преподаватели добавляют индексы к переменным кортежа, если они хотят ссылаться на элементы кортежа. И общая (латексная) нотация для записи "с индексом n" (имея в виду n-й элемент кортежа) составляет _n. Так что я нахожу это на самом деле очень интуитивным.