Es ist nicht möglich (außer "von Hand") AFAIK. In Anbetracht def toTuple(x: List[Int]): R, was sollte die Art der R?
7
Wenn dies für ein Tupel beliebiger Größe nicht erforderlich ist (dh wie bei einer oben dargestellten hypothetischen Methode), berücksichtigen Sie x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }und beachten Sie, dass der resultierende Typ aufTuple3[Int,Int,Int]
2
Während das, was Sie tun Listmöchten, nicht möglich ist , können Sie sich den HListTyp Shapeless ansehen , der die Konvertierung in und von Tupeln ermöglicht ( github.com/milessabin/… ). Vielleicht ist es für Ihren Anwendungsfall anwendbar.
Antworten:
57
Sie können dies nicht typsicher tun. Warum? Weil wir im Allgemeinen die Länge einer Liste erst zur Laufzeit kennen. Die "Länge" eines Tupels muss jedoch in seinem Typ codiert sein und ist daher zur Kompilierungszeit bekannt. Zum Beispiel (1,'a',true)hat der Typ (Int, Char, Boolean), für den Zucker ist Tuple3[Int, Char, Boolean]. Der Grund, warum Tupel diese Einschränkung haben, ist, dass sie in der Lage sein müssen, mit inhomogenen Typen umzugehen.
Gibt es eine Liste fester Größe von Elementen mit demselben Typ? Natürlich keine nicht standardmäßigen Bibliotheken wie formlose importieren.
1
@davips nicht, dass ich weiß, aber es ist einfach genug, Ihre eigenen zu machen, zBcase class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett
Dies wäre ein "Tupel" von Elementen desselben Typs. Ich kann beispielsweise keine Karte darauf anwenden.
1
@davips wie bei jedem neuen Datentyp müssten Sie definieren, wie mapetc dafür funktioniert
Tom Crockett
1
Diese Art der Einschränkung scheint vor allem ein eindeutiger Indikator für die Nutzlosigkeit der Typensicherheit zu sein. Es erinnert mich an das Zitat "Die leere Menge hat viele interessante Eigenschaften."
Ely
57
Sie können dies mit Scala-Extraktoren und Pattern Matching ( Link ) tun :
val x =List(1,2,3)val t = x match{caseList(a, b, c)=>(a, b, c)}
Welches gibt ein Tupel zurück
t:(Int,Int,Int)=(1,2,3)
Sie können auch einen Platzhalteroperator verwenden, wenn Sie sich über die Größe der Liste nicht sicher sind
Im Zusammenhang mit dieser Lösung führen Beschwerden über die Typensicherheit dazu, dass Sie zur Laufzeit möglicherweise einen MatchError erhalten. Wenn Sie möchten, können Sie versuchen, diesen Fehler abzufangen und etwas zu tun, wenn die Liste die falsche Länge hat. Ein weiteres nettes Merkmal dieser Lösung ist, dass die Ausgabe kein Tupel sein muss, sondern eine Ihrer eigenen benutzerdefinierten Klassen oder eine Transformation der Zahlen sein kann. Ich glaube jedoch nicht, dass Sie dies mit Nicht-Listen tun können (daher müssen Sie möglicherweise eine .toList-Operation für die Eingabe ausführen).
Jim Pivarski
Sie können dies mit jeder Art von Scala-Sequenz mit der Syntax +: tun. Vergessen Sie auch nicht, ein Allheilmittel hinzuzufügen
import shapeless._import syntax.std.traversable._val x =List(1,2,3)val xHList = x.toHList[Int::Int::Int::HNil]val t = xHList.get.tupled
Hinweis: Der Compiler benötigt einige Typinformationen, um die Liste in der HList zu konvertieren. Dies ist der Grund, warum Sie Typinformationen an die toHListMethode übergeben müssen
Shapeless 2.0 hat die Syntax geändert. Hier ist die aktualisierte Lösung mit Shapeless.
import shapeless._importHList._import syntax.std.traversable._val x =List(1,2,3)val y = x.toHList[Int::Int::Int::HNil]val z = y.get.tupled
Das Hauptproblem besteht darin, dass der Typ für .toHList im Voraus angegeben werden muss. Da Tupel in ihrer Arität begrenzt sind, kann das Design Ihrer Software im Allgemeinen besser von einer anderen Lösung unterstützt werden.
Wenn Sie jedoch eine Liste statisch erstellen, sollten Sie eine Lösung wie diese in Betracht ziehen, die auch formlos ist. Hier erstellen wir direkt eine HList und der Typ ist zur Kompilierungszeit verfügbar. Denken Sie daran, dass eine HList Funktionen sowohl vom Listen- als auch vom Tupeltyp enthält. Das heißt, es kann Elemente mit unterschiedlichen Typen wie ein Tupel haben und kann unter anderen Operationen wie Standardsammlungen zugeordnet werden. HListen brauchen eine Weile, um sich daran zu gewöhnen, also treten Sie langsam, wenn Sie neu sind.
Trotz der Einfachheit und nicht für Listen beliebiger Länge geeignet, ist es typsicher und in den meisten Fällen die Antwort:
val list =List('a','b')val tuple = list(0)-> list(1)val list =List('a','b','c')val tuple =(list(0), list(1), list(2))
Eine andere Möglichkeit, wenn Sie die Liste nicht benennen oder wiederholen möchten (ich hoffe, jemand kann einen Weg zeigen, um die Seq / head-Teile zu vermeiden):
val tuple =Seq(List('a','b')).map(tup => tup(0)-> tup(1)).head
val tuple =Seq(List('a','b','c')).map(tup =>(tup(0), tup(1), tup(2))).head
@javadba Ich habe nach einer Implementierung von listToTuple () gesucht, musste sie aber nicht. Die Liste syntaktischer Zucker löste mein Problem.
Peter L
Es gibt auch eine andere Syntax, die meiner Meinung nach etwas besser aussieht:val List(c1, c2, c3) = myList
Kolmar
7
Sie können dies nicht typsicher tun. In Scala sind Listen Sequenzen beliebiger Länge von Elementen eines Typs. Soweit das Typsystem weiß, xkönnte es sich um eine Liste beliebiger Länge handeln.
Im Gegensatz dazu muss die Arität eines Tupels zur Kompilierungszeit bekannt sein. Es würde die Sicherheitsgarantien des Typsystems verletzen, die Zuordnung xzu einem Tupeltyp zu ermöglichen .
Es wäre möglich, eine Funktion manuell zu codieren, die Listen mit bis zu 22 Elementen konvertiert und eine Ausnahme für größere Listen auslöst. Die Vorlagenunterstützung von Scala, eine bevorstehende Funktion, würde dies präziser machen. Aber das wäre ein hässlicher Hack.
Es ist typsicher: Sie erhalten None, wenn die Tupelgröße falsch ist, aber die Tupelgröße muss ein Literal sein oder final val(um konvertierbar zu sein shapeless.Nat).
Dies scheint nicht für Größen größer als 22 zu funktionieren, zumindest für
shapeeless_2.11
@kfkhalili Ja, und es ist keine formlose Einschränkung. Scala 2 selbst erlaubt keine Tupel mit mehr als 22 Elementen, daher ist es mit keiner Methode möglich, eine Liste größerer Größe in ein Tupel zu konvertieren.
Kolmar
4
Wenn Sie sehr sicher sind, dass Ihre list.size <23 verwendet wird:
Wenn Sie eine so alte Frage beantworten (über 6 Jahre), ist es oft eine gute Idee, darauf hinzuweisen, dass sich Ihre Antwort von allen (12) älteren Antworten unterscheidet, die bereits eingereicht wurden. Insbesondere gilt Ihre Antwort nur, wenn die Länge des List/ Tupleeine bekannte Konstante ist.
Jwvh
Vielen Dank @ jwvh, ich werde Ihre Kommentare in Zukunft berücksichtigen.
Michael Olafisoye
2
Beitrag 2015. Damit die Antwort von Tom Crockett klarer wird , hier ein echtes Beispiel.
Zuerst war ich verwirrt. Weil ich aus Python komme, wo du es einfach machen kannst tuple(list(1,2,3)).
Fehlt es an Scala-Sprache? (Die Antwort lautet: Es geht nicht um Scala oder Python, es geht um statischen und dynamischen Typ.)
Das bringt mich dazu, den Kern zu finden, warum Scala das nicht kann.
Das folgende Codebeispiel implementiert eine toTupleMethode, die typsicher toTupleNund typsicher ist toTuple.
Die toTupleMethode ruft die Informationen zur Typlänge zur Laufzeit ab, dh keine Informationen zur Typlänge zur Kompilierungszeit, sodass der Rückgabetyp Productdem des Pythons sehr ähnlich ist tuple(kein Typ an jeder Position und keine Länge der Typen).
Auf diese Weise kommt es zu Laufzeitfehlern wie Typinkongruenz oder IndexOutOfBoundException. (Pythons praktisches List-to-Tupel ist also kein kostenloses Mittagessen.)
Im Gegensatz dazu ist es die vom Benutzer bereitgestellte Längeninformation, die die toTupleNKompilierungszeit sicher macht.
implicitclassEnrichedWithToTuple[A](elements:Seq[A]){def toTuple:Product= elements.length match{case2=> toTuple2
case3=> toTuple3
}def toTuple2 = elements match{caseSeq(a, b)=>(a, b)}def toTuple3 = elements match{caseSeq(a, b, c)=>(a, b, c)}}val product =List(1,2,3).toTuple
product.productElement(5)//runtime IndexOutOfBoundException, Bad ! val tuple =List(1,2,3).toTuple3
tuple._5 //compiler error, Good!
def toTuple(x: List[Int]): R
, was sollte die Art der R?x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
und beachten Sie, dass der resultierende Typ aufTuple3[Int,Int,Int]
List
möchten, nicht möglich ist , können Sie sich denHList
Typ Shapeless ansehen , der die Konvertierung in und von Tupeln ermöglicht ( github.com/milessabin/… ). Vielleicht ist es für Ihren Anwendungsfall anwendbar.Antworten:
Sie können dies nicht typsicher tun. Warum? Weil wir im Allgemeinen die Länge einer Liste erst zur Laufzeit kennen. Die "Länge" eines Tupels muss jedoch in seinem Typ codiert sein und ist daher zur Kompilierungszeit bekannt. Zum Beispiel
(1,'a',true)
hat der Typ(Int, Char, Boolean)
, für den Zucker istTuple3[Int, Char, Boolean]
. Der Grund, warum Tupel diese Einschränkung haben, ist, dass sie in der Lage sein müssen, mit inhomogenen Typen umzugehen.quelle
case class Vec3[A](_1: A, _2: A, _3: A)
map
etc dafür funktioniertSie können dies mit Scala-Extraktoren und Pattern Matching ( Link ) tun :
Welches gibt ein Tupel zurück
Sie können auch einen Platzhalteroperator verwenden, wenn Sie sich über die Größe der Liste nicht sicher sind
quelle
ein Beispiel mit formlos :
Hinweis: Der Compiler benötigt einige Typinformationen, um die Liste in der HList zu konvertieren. Dies ist der Grund, warum Sie Typinformationen an die
toHList
Methode übergeben müssenquelle
Shapeless 2.0 hat die Syntax geändert. Hier ist die aktualisierte Lösung mit Shapeless.
Das Hauptproblem besteht darin, dass der Typ für .toHList im Voraus angegeben werden muss. Da Tupel in ihrer Arität begrenzt sind, kann das Design Ihrer Software im Allgemeinen besser von einer anderen Lösung unterstützt werden.
Wenn Sie jedoch eine Liste statisch erstellen, sollten Sie eine Lösung wie diese in Betracht ziehen, die auch formlos ist. Hier erstellen wir direkt eine HList und der Typ ist zur Kompilierungszeit verfügbar. Denken Sie daran, dass eine HList Funktionen sowohl vom Listen- als auch vom Tupeltyp enthält. Das heißt, es kann Elemente mit unterschiedlichen Typen wie ein Tupel haben und kann unter anderen Operationen wie Standardsammlungen zugeordnet werden. HListen brauchen eine Weile, um sich daran zu gewöhnen, also treten Sie langsam, wenn Sie neu sind.
quelle
Trotz der Einfachheit und nicht für Listen beliebiger Länge geeignet, ist es typsicher und in den meisten Fällen die Antwort:
Eine andere Möglichkeit, wenn Sie die Liste nicht benennen oder wiederholen möchten (ich hoffe, jemand kann einen Weg zeigen, um die Seq / head-Teile zu vermeiden):
quelle
FWIW, ich wollte ein Tupel, um eine Reihe von Feldern zu initialisieren, und wollte den syntaktischen Zucker der Tupelzuweisung verwenden. Z.B:
Es stellt sich heraus, dass es syntaktischen Zucker gibt, um auch den Inhalt einer Liste zuzuweisen ...
Sie brauchen also keine Tupel, wenn Sie das gleiche Problem haben.
quelle
val List(c1, c2, c3) = myList
Sie können dies nicht typsicher tun. In Scala sind Listen Sequenzen beliebiger Länge von Elementen eines Typs. Soweit das Typsystem weiß,
x
könnte es sich um eine Liste beliebiger Länge handeln.Im Gegensatz dazu muss die Arität eines Tupels zur Kompilierungszeit bekannt sein. Es würde die Sicherheitsgarantien des Typsystems verletzen, die Zuordnung
x
zu einem Tupeltyp zu ermöglichen .Aus technischen Gründen waren Scala-Tupel auf 22 Elemente beschränkt , aber das Limit existiert in 2.11 nicht mehr. Das Limit für Fallklassen wurde in 2.11 https://github.com/scala/scala/pull/2305 aufgehoben
Es wäre möglich, eine Funktion manuell zu codieren, die Listen mit bis zu 22 Elementen konvertiert und eine Ausnahme für größere Listen auslöst. Die Vorlagenunterstützung von Scala, eine bevorstehende Funktion, würde dies präziser machen. Aber das wäre ein hässlicher Hack.
quelle
Dies kann auch
shapeless
mit weniger Boilerplate erfolgen, indemSized
:Es ist typsicher: Sie erhalten
None
, wenn die Tupelgröße falsch ist, aber die Tupelgröße muss ein Literal sein oderfinal val
(um konvertierbar zu seinshapeless.Nat
).quelle
Wenn Sie sehr sicher sind, dass Ihre list.size <23 verwendet wird:
quelle
Verwenden der Musteranpassung:
val intTuple = Liste (1,2,3) entspricht {Fallliste (a, b, c) => (a, b, c)}
quelle
List
/Tuple
eine bekannte Konstante ist.Beitrag 2015. Damit die Antwort von Tom Crockett klarer wird , hier ein echtes Beispiel.
Zuerst war ich verwirrt. Weil ich aus Python komme, wo du es einfach machen kannst
tuple(list(1,2,3))
.Fehlt es an Scala-Sprache? (Die Antwort lautet: Es geht nicht um Scala oder Python, es geht um statischen und dynamischen Typ.)
Das bringt mich dazu, den Kern zu finden, warum Scala das nicht kann.
Das folgende Codebeispiel implementiert eine
toTuple
Methode, die typsichertoTupleN
und typsicher isttoTuple
.Die
toTuple
Methode ruft die Informationen zur Typlänge zur Laufzeit ab, dh keine Informationen zur Typlänge zur Kompilierungszeit, sodass der RückgabetypProduct
dem des Pythons sehr ähnlich isttuple
(kein Typ an jeder Position und keine Länge der Typen).Auf diese Weise kommt es zu Laufzeitfehlern wie Typinkongruenz oder
IndexOutOfBoundException
. (Pythons praktisches List-to-Tupel ist also kein kostenloses Mittagessen.)Im Gegensatz dazu ist es die vom Benutzer bereitgestellte Längeninformation, die die
toTupleN
Kompilierungszeit sicher macht.quelle
Sie können dies auch tun
indem Sie die Liste durchlaufen und jedes Element einzeln anwenden.
quelle
soweit du den typ hast:
quelle