En mathématiques et en informatique, un tuple est une liste ordonnée d'éléments. En théorie des ensembles, un n-Tuple (ordonné) est une séquence (ou une liste ordonnée) de n éléments, où n est un entier positif.
Ainsi, par exemple, en Python, le deuxième élément d’un tuple serait accessible via t[1]
.
En Scala, l'accès n'est possible que via des noms étranges t._2
.
La question est donc: pourquoi ne puis-je pas accéder aux données sous forme de nuplets sous forme de séquence ou de liste si c'est par définition? Existe-t-il une idée ou n'est-il pas encore inspecté?
Scala connaît l’arité des n-uplets et est donc capable de fournir des accesseurs tels que _1
, _2
, etc., et de générer une erreur de compilation si vous sélectionnez _3
sur une paire, par exemple. De plus, le type de ces champs correspond exactement au type utilisé comme paramètre pour Tuple
(par exemple, _3
sur un Tuple3[Int, Double, Float]
retournera un Float
).
Si vous souhaitez accéder au nième élément, vous pouvez écrire Tuple.productElement(n)
, mais le type de retour correspondant ne peut être que Any
. Vous perdez donc les informations de type.
Je crois que l'extrait suivant de "Programmer en Scala: guide étape par étape complet" (Martin Odersky, Lex Spoon et Bill Venners) répond directement à vos deux questions:
Accéder aux éléments d'un tuple
Vous vous demandez peut-être pourquoi vous ne pouvez pas accéder aux éléments d’un tuple tel que les éléments d'une liste, par exemple, avec "paire (0)". La raison est que la méthode apply d'une liste retourne toujours le même type, mais chaque L'élément d'un Tuple peut être d'un type différent: _1 peut avoir un résultat tapez, _2 un autre, et ainsi de suite. Ces numéros _N sont à base unique, à la place de base zéro, car à partir de 1 est une tradition définie par autre langues avec des n-uplets typés statiquement, tels que Haskell et ML.
Les tuples Scala reçoivent très peu de traitement préférentiel en ce qui concerne la syntaxe du langage, à part que les expressions '(' a1, ..., an ')'
sont traitées par le compilateur comme un alias pour l'instanciation de classe scala.Tuplen (a1, ...,). Sinon, les tuples se comportent comme n'importe quel autre objet Scala. En fait, ils sont écrits dans Scala sous la forme les classes de cas allant de Tuple2 à Tuple22 . Tuple2 et Tuple3 sont également connus sous les alias de Pair et Triple respectivement:
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)
Une grande différence entre List
, Seq
ou toute collection et Tuple est que dans Tuple, chaque élément a son propre type, alors que dans List tous les éléments ont le même type.
Et par conséquent, dans Scala, vous trouverez des classes telles que Tuple2[T1, T2]
ou Tuple3[T1, T2, T3]
. Ainsi, pour chaque élément, vous avez également le paramètre type. Les collections n'acceptent qu'un seul paramètre de type: List[T]
. La syntaxe comme ("Test", 123, new Date)
est simplement un sucre syntaxique pour Tuple3[String, Int, Date]
. Et _1
, _2
, etc. ne sont que des champs sur Tuple qui retournent l’élément correspondant.
Vous pouvez facilement y arriver avec informe :
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
De nombreuses méthodes disponibles pour la collection standard sont également disponibles pour les n-uplets de cette manière (head
, tail
, init
, last
, ++
et :::
pour la concaténation, +:
et :+
pour ajout d'éléments, take
, drop
, reverse
, Zip
, unzip
, length
, toList
, toArray
, to[Collection]
, ...)
Je pense que c'est pour la vérification de type. Comme le dit Delnan, si vous avez un tuple t
et un index e
(une expression arbitraire), t(e)
ne donnera au compilateur aucune information sur l'élément utilisé (ou même s'il s'agit d'un élément valide pour un tuple de cette taille). Lorsque vous accédez aux éléments par nom de champ (_2
est un identifiant valide, ce n'est pas une syntaxe spéciale), le compilateur sait à quel champ vous accédez et de quel type il est. Les langages comme Python n'ont pas vraiment de types, donc ce n'est pas nécessaire pour eux.
Avec un accès normal à l’index, toute expression peut être utilisée, et il faudrait un effort sérieux pour vérifier à compiletime si le résultat de l’expression de l’index est dans la plage. Faites-en un attribut, et une erreur de compilation pour (1, 2)._3
suit "gratuitement". Des choses comme autoriser uniquement les constantes de nombre entier dans l’accès à l’élément sur des n-uplets constitueraient un cas très particulier (moche et inutile, certains diraient même ridicule) et encore du travail à implémenter dans le compilateur.
Python, par exemple, peut s'en tirer, car il ne pourrait pas (ne pourrait pas) vérifier (au moment de compiletime) si l'index est dans la plage de toute façon.
Outre les avantages déjà mentionnés par Jean-Philippe Pellet, cette notation est également très courante en mathématiques (voir http://en.wikipedia.org/wiki/Tuple ). Beaucoup de conférenciers ajoutent des index aux variables Tuple s’ils veulent faire référence aux éléments d’un Tuple. Et la notation commune (LaTeX) pour écrire "avec index n" (en référence au n -ième élément du tuple) est _n
. Je trouve donc cela très intuitif.