Warum ist Scalas Syntax für Tupel so ungewöhnlich?


72

In der Mathematik und Informatik ist ein Tupel eine geordnete Liste von Elementen. In der Mengenlehre ist ein (geordnetes) n-Tupel eine Folge (oder geordnete Liste) von n Elementen, wobei n eine positive ganze Zahl ist.

So würde beispielsweise in Python auf das zweite Element eines Tupels über zugegriffen t[1].

In Scala ist der Zugriff nur über fremde Namen möglich t._2.

Die Frage ist also, warum ich nicht auf Daten in Tupeln als Sequenz oder Liste zugreifen kann, wenn dies per Definition der Fall ist. Gibt es eine Idee oder nur noch nicht geprüft?

Antworten:


92

Scala kennt die arity der Tupel und ist somit in der Lage Accessoren dergleichen zu schaffen _1, _2usw. und einen Fehler bei der Kompilierung erzeugen , wenn Sie wählen _3ein Paar, zum Beispiel. Darüber hinaus ist der Typ dieser Felder genau der Typ, für den der Typ als Parameter verwendet wird Tuple(z. B. _3bei a Tuple3[Int, Double, Float]wird a zurückgegeben Float).

Wenn Sie auf das n-te Element zugreifen möchten, können Sie schreiben tuple.productElement(n), der Rückgabetyp kann jedoch nur sein Any, sodass Sie die Typinformationen verlieren.


1
Ich erinnere mich, dass ich gelesen habe, dass es jetzt, da die Vererbung von Fallklassen illegal ist, möglich ist, den Typ zu verstärken, productIteratorsodass in einigen Fällen ein spezifischerer Typ verwendet werden kann. Dies könnte für 2.10 kommen, aber jemand korrigiert mich, wenn ich falsch liege.
Kipton Barros

1
Es wäre für den Compiler möglich, einen Ausdruck wie t[1]alle Typinformationen zuzulassen und zu behalten und einfach t[exp](wobei das Ergebnis von exp zur Kompilierungszeit unbekannt ist) einen Rückgabetyp von zu haben Any, oder? Es scheint also, dass die andere Syntax eher dazu dient, dem Programmierer klar zu machen, dass ein Tupel keine Liste ist.
Benzado

@benzado Sie t[exp]sind genau das, was t.productElement (exp) `tut. Beachten Sie, dass in Scala eckige Klammern nur für Typparameter verwendet werden.
Jean-Philippe Pellet

@Kipton Barros: So funktioniert es bereits -Xexperimentalin 2.10 Trunk, aber nur für Fallklassen , nicht für Tupel. Keine Ahnung warum.
Soc

5
Warum dann nicht die Methode apply () unterstützen? List, Array und alle anderen Typen unterstützen dies. Scheint seltsam, dass Tuple * die Apply-Methode nicht unterstützt (was das Schreiben von Tupl (0), Tupl (3) usw. so viel einfacher gemacht hätte). IMHO, das hätte es mit anderen Typen in der Sprache einheitlicher gemacht.
Ajay

37

Ich glaube, der folgende Auszug aus "Programmieren in Scala: Eine umfassende Schritt-für-Schritt-Anleitung" (Martin Odersky, Lex Spoon und Bill Venners) geht direkt auf Ihre beiden Fragen ein:

Zugriff auf die Elemente eines Tupels

Sie fragen sich vielleicht, warum Sie nicht auf die Elemente eines Tupels wie die Elemente einer Liste zugreifen können, z. B. mit "pair (0)". Der Grund dafür ist, dass die Apply-Methode einer Liste immer denselben Typ zurückgibt, aber jedes Element eines Tupels ein anderer Typ sein kann: _1 kann einen Ergebnistyp haben, _2 einen anderen und so weiter. Diese _N-Zahlen basieren auf Eins statt auf Null, da das Beginnen mit 1 eine Tradition ist, die von anderen Sprachen mit statisch typisierten Tupeln wie Haskell und ML festgelegt wird.

Scala-Tupel werden in Bezug auf die Sprachsyntax kaum bevorzugt behandelt, abgesehen davon, dass Ausdrücke '(' a1, ..., an ')'vom Compiler als Alias ​​für die Instanziierung von scala.Tuplen ( a1, ..., an ) behandelt werden. Andernfalls verhalten sich Tupel wie alle anderen Scala-Objekte. Tatsächlich werden sie in Scala als Fallklassen geschrieben , die von Tuple2 bis Tuple22 reichen . Tuple2 und Tuple3 sind auch unter den Decknamen Pair und Triple bekannt:

 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)

6
Paar und Dreifach sind jetzt veraltet. Sie wurden in Scala 2.11
Dave

1
Inzwischen Tuple1wurde hinzugefügt.
David Moles

20

Ein großer Unterschied zwischen List, Seqoder jede Sammlung und Tupel in Tupel , dass jedes Element seine eigene Art hat , wo die in der Liste alle Elemente denselben Typ haben.

Infolgedessen finden Sie in Scala Klassen wie Tuple2[T1, T2]oder Tuple3[T1, T2, T3], sodass Sie für jedes Element auch einen Typparameter haben. Sammlungen akzeptieren nur 1 Typparameter : List[T]. Syntax wie ("Test", 123, new Date)ist nur syntaktischer Zucker für Tuple3[String, Int, Date]. Und _1, _2usw. sind nur Felder auf Tupel dass die Rückkehr Korrespondent Element.


1
Dies ist eine viel bessere Antwort als die anderen hier. Der entscheidende Punkt ist, dass Listen homogene Typen erzwingen, während Tupel dies nicht tun, was die Typprüfung schwieriger macht.
Bcherny

12

Sie können das leicht mit formlos erreichen :

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

Viele Methoden zur Standardsammlung sind auch für diese Weise Tupeln (verfügbar head, tail, init, last, ++und :::für die Verkettung, +:und :+Elemente für das Hinzufügen, take, drop, reverse, zip, unzip, length, toList, toArray, to[Collection], ...)


7
Ein möglicher Nachteil besteht darin, dass die Kompilierungszeiten länger sein können, da dies eine Programmierung auf "Typebene" umfasst, dh Berechnungen, die über das Typsystem ausgedrückt und impliziert werden und von Scalac durchgeführt werden. Eine zusätzliche Funktion, die sowohl ein Vorteil als auch ein Nachteil ist, besteht darin, dass das Aufrufen dieser Methoden, wenn Sie nicht kompilieren können, nicht kompiliert werden kann (z. B. das Aufrufen von t.take (5) im obigen Beispiel), Sie jedoch normalerweise eine seltsame Fehlermeldung erhalten Skalac anstelle einer klaren Erklärung. Ein wenig Arbeit in formlos kann diesen letzten Punkt ein wenig besser machen
Alex Archambault

8

Ich denke, es ist für die Typprüfung. Wie delnan sagt, wenn Sie ein Tupel tund einen Index e(einen beliebigen Ausdruck) haben, t(e)würde der Compiler keine Informationen darüber erhalten, auf welches Element zugegriffen wird (oder selbst wenn es ein gültiges Element für ein Tupel dieser Größe ist). Wenn Sie über den Feldnamen auf Elemente zugreifen (dies _2ist eine gültige Kennung, keine spezielle Syntax), weiß der Compiler, auf welches Feld Sie zugreifen und welchen Typ es hat. Sprachen wie Python haben eigentlich keine Typen, daher ist dies für sie nicht erforderlich.


7

Bei normalem Indexzugriff kann jeder Ausdruck verwendet werden, und es würde einige ernsthafte Anstrengungen erfordern, um beim Kompilieren zu überprüfen, ob das Ergebnis des Indexausdrucks garantiert im Bereich liegt. Machen Sie es zu einem Attribut und ein Fehler zur Kompilierungszeit für (1, 2)._3folgt "kostenlos". Dinge wie das Zulassen nur ganzzahliger Konstanten innerhalb des Elementzugriffs auf Tupel wären ein ganz besonderer Fall (hässlich und unnötig, manche würden sagen lächerlich) und wieder einige Arbeiten, die im Compiler implementiert werden müssen.

Python zum Beispiel kann damit durchkommen, weil es (zum Zeitpunkt der Kompilierung) nicht prüfen würde (konnte), ob der Index ohnehin im Bereich liegt.


0

Abgesehen von den bereits erwähnten Vorteilen von Jean-Philippe Pellet ist diese Notation auch in der Mathematik sehr verbreitet (siehe http://en.wikipedia.org/wiki/Tuple ). Viele Dozenten hängen Indizes an Tupelvariablen an, wenn sie sich auf die Elemente eines Tupels beziehen möchten. Und die übliche (LaTeX) Notation zum Schreiben von "mit Index n " (bezogen auf das n- te Element des Tupels) lautet _n. Also ich finde es eigentlich sehr intuitiv.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.