Itu tidak mungkin (kecuali "dengan tangan") AFAIK. Diberikan def toTuple(x: List[Int]): R, apa yang seharusnya menjadi tipe R?
7
Jika tidak perlu dilakukan untuk tupel berukuran sewenang-wenang (mis. Seperti metode hipotetis yang disajikan di atas), pertimbangkan x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }dan catat jenis yang dihasilkan ditetapkan diTuple3[Int,Int,Int]
2
Meskipun apa yang ingin Anda lakukan tidak memungkinkan List, Anda dapat melihat HListtipe Shapeless ' , yang memungkinkan konversi ke dan dari tupel ( github.com/milessabin/… ). Mungkin itu berlaku untuk kasus penggunaan Anda.
Jawaban:
59
Anda tidak dapat melakukan ini dengan cara yang aman. Mengapa? Karena secara umum kita tidak bisa mengetahui panjang list sampai runtime. Tetapi "panjang" dari sebuah tupel harus dikodekan dalam tipenya, dan karenanya diketahui pada waktu kompilasi. Misalnya (1,'a',true)memiliki jenis (Int, Char, Boolean)gula untuk Tuple3[Int, Char, Boolean]. Alasan tupel memiliki batasan ini adalah karena mereka harus dapat menangani tipe yang tidak homogen.
Apakah ada daftar elemen berukuran tetap dengan tipe yang sama? Tidak mengimpor pustaka yang tidak standar, seperti tidak berbentuk, tentunya.
1
@davips bukan yang saya tahu, tapi cukup mudah untuk membuatnya sendiri, misalnyacase class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett
Ini akan menjadi "tuple" elemen dengan tipe yang sama, saya tidak bisa menerapkan peta di atasnya, misalnya.
1
@davips seperti halnya tipe data baru Anda harus menentukan bagaimana mapdll bekerja untuknya
Tom Crockett
1
Batasan semacam ini tampaknya menjadi indikator kuat dari tidak bergunanya keamanan tipe lebih dari apapun. Ini mengingatkan saya pada kutipan "himpunan kosong memiliki banyak properti menarik."
ely
58
Anda dapat melakukannya menggunakan ekstraktor skala dan pencocokan pola ( tautan ):
val x = List(1, 2, 3)
val t = x match {
caseList(a, b, c) => (a, b, c)
}
Yang mengembalikan tupel
t: (Int, Int, Int) = (1,2,3)
Selain itu, Anda dapat menggunakan operator karakter pengganti jika tidak yakin tentang ukuran List
val t = x match {
caseList(a, b, c, _*) => (a, b, c)
}
Dalam konteks solusi ini, keluhan tentang type-safety berarti Anda mungkin mendapatkan MatchError saat runtime. Jika mau, Anda dapat mencoba menangkap kesalahan itu dan melakukan sesuatu jika Panjang Daftar salah. Fitur bagus lainnya dari solusi ini adalah bahwa hasilnya tidak harus berupa tupel, ini bisa berupa salah satu kelas khusus Anda sendiri atau beberapa transformasi angka. Saya tidak berpikir Anda dapat melakukan ini dengan non-List, meskipun (jadi Anda mungkin perlu melakukan operasi .toList ke input).
Jim Pivarski
Anda dapat melakukan ini dengan semua jenis urutan Scala menggunakan sintaks + :. Selain itu, jangan lupa untuk menambahkan tujuan umum
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
Catatan: kompilator memerlukan beberapa informasi tipe untuk mengubah Daftar di HList yang menjadi alasan mengapa Anda perlu meneruskan informasi tipe ke toHListmetode
Shapeless 2.0 mengubah beberapa sintaks. Inilah solusi yang diperbarui menggunakan tidak berbentuk.
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
Masalah utamanya adalah tipe untuk .toHList harus ditentukan sebelumnya. Secara lebih umum, karena tupel terbatas dalam aritasnya, desain perangkat lunak Anda mungkin lebih baik disajikan dengan solusi yang berbeda.
Namun, jika Anda membuat daftar secara statis, pertimbangkan solusi seperti ini, juga menggunakan tak berbentuk. Di sini, kami membuat HList secara langsung dan jenisnya tersedia pada waktu kompilasi. Ingatlah bahwa HList memiliki fitur dari tipe List dan Tuple. yaitu dapat memiliki elemen dengan tipe berbeda seperti Tuple dan dapat dipetakan di antara operasi lain seperti koleksi standar. HLists membutuhkan sedikit waktu untuk membiasakan diri, jadi melangkahlah perlahan jika Anda baru.
Terlepas dari kesederhanaan dan tidak untuk daftar dengan panjang apa pun, itu aman untuk tipe dan jawabannya dalam banyak kasus:
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))
Kemungkinan lain, ketika Anda tidak ingin menamai daftar atau mengulanginya (saya harap seseorang dapat menunjukkan cara untuk menghindari Seq / bagian kepala):
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 Saya sedang mencari cara menerapkan listToTuple () tetapi akhirnya tidak perlu. Daftar gula sintaksis memecahkan masalah saya.
Peter L
Ada juga sintaks lain, yang menurut saya terlihat sedikit lebih baik:val List(c1, c2, c3) = myList
Kolmar
7
Anda tidak dapat melakukan ini dengan cara yang aman. Di Scala, daftar adalah urutan elemen dengan panjang arbitrer dari beberapa jenis. Sejauh yang diketahui sistem tipe,x bisa menjadi daftar panjang sewenang-wenang.
Sebaliknya, arity dari sebuah tupel harus diketahui pada waktu kompilasi. Ini akan melanggar jaminan keamanan dari sistem tipe untuk memungkinkan penetapanx ke tipe tupel.
Dimungkinkan untuk mengkodekan fungsi secara manual yang mengonversi daftar hingga 22 elemen, dan memunculkan pengecualian untuk daftar yang lebih besar. Dukungan template Scala, fitur yang akan datang, akan membuatnya lebih ringkas. Tapi ini akan menjadi peretasan yang buruk.
Tampaknya ini tidak berfungsi untuk ukuran yang lebih besar dari 22, setidaknya untuk tak berbentuk_2.11: 2.3.3
kfkhalili
@kfkhalili Ya, dan ini bukan batasan tak berbentuk. Scala 2 sendiri tidak mengizinkan tupel dengan lebih dari 22 elemen, jadi tidak mungkin untuk mengonversi Daftar dengan ukuran lebih besar ke Tupel menggunakan metode apa pun.
Kolmar
4
Menggunakan Pencocokan Pola:
val intTuple = List(1,2,3) match {caseList(a, b, c) => (a, b, c)}
Saat menjawab pertanyaan yang sudah berumur (lebih dari 6 tahun) ini seringkali merupakan ide yang baik untuk menunjukkan bagaimana jawaban Anda berbeda dari semua (12) jawaban lama yang sudah dikirimkan. Secara khusus, jawaban Anda hanya berlaku jika panjang List/ Tupleadalah konstanta yang diketahui.
jwvh
Terima kasih @ jwvh, akan mempertimbangkan komentar Anda ke depannya.
Michael Olafisoye
2
2015 pos. Agar jawaban Tom Crockett lebih memperjelas , berikut adalah contoh nyata.
Awalnya saya bingung. Karena saya berasal dari Python, di mana Anda bisa melakukannya tuple(list(1,2,3)).
Apakah ini pendek dari bahasa Scala? (jawabannya adalah - ini bukan tentang Scala atau Python, ini tentang tipe statis dan tipe dinamis.)
Itulah yang menyebabkan saya mencoba menemukan inti mengapa Scala tidak dapat melakukan ini.
Contoh kode berikut mengimplementasikan sebuah toTuplemetode, yang memiliki tipe-aman toTupleNdan tipe-tidak aman toTuple.
The toTupleMetode mendapatkan informasi jenis-panjang pada saat run-time, yaitu tidak ada informasi jenis-panjang pada saat kompilasi, sehingga jenis kembali adalah Productyang sangat seperti Python tuplememang (ada jenis pada setiap posisi, dan tidak ada panjang jenis).
Cara itu rentan terhadap error runtime seperti type-mismatch atau IndexOutOfBoundException. (jadi list-to-tuple Python yang nyaman bukanlah makan siang gratis.)
Sebaliknya, panjang informasi yang disediakan pengguna yang membuat toTupleNwaktu kompilasi aman.
implicitclassEnrichedWithToTuple[A](elements: Seq[A]) {
deftoTuple: Product = elements.length match {
case2 => toTuple2
case3 => toTuple3
}
deftoTuple2= elements match {caseSeq(a, b) => (a, b) }
deftoTuple3= 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
, apa yang seharusnya menjadi tipe R?x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
dan catat jenis yang dihasilkan ditetapkan diTuple3[Int,Int,Int]
List
, Anda dapat melihatHList
tipe Shapeless ' , yang memungkinkan konversi ke dan dari tupel ( github.com/milessabin/… ). Mungkin itu berlaku untuk kasus penggunaan Anda.Jawaban:
Anda tidak dapat melakukan ini dengan cara yang aman. Mengapa? Karena secara umum kita tidak bisa mengetahui panjang list sampai runtime. Tetapi "panjang" dari sebuah tupel harus dikodekan dalam tipenya, dan karenanya diketahui pada waktu kompilasi. Misalnya
(1,'a',true)
memiliki jenis(Int, Char, Boolean)
gula untukTuple3[Int, Char, Boolean]
. Alasan tupel memiliki batasan ini adalah karena mereka harus dapat menangani tipe yang tidak homogen.sumber
case class Vec3[A](_1: A, _2: A, _3: A)
map
dll bekerja untuknyaAnda dapat melakukannya menggunakan ekstraktor skala dan pencocokan pola ( tautan ):
val x = List(1, 2, 3) val t = x match { case List(a, b, c) => (a, b, c) }
Yang mengembalikan tupel
t: (Int, Int, Int) = (1,2,3)
Selain itu, Anda dapat menggunakan operator karakter pengganti jika tidak yakin tentang ukuran List
val t = x match { case List(a, b, c, _*) => (a, b, c) }
sumber
sebuah contoh menggunakan tak berbentuk :
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
Catatan: kompilator memerlukan beberapa informasi tipe untuk mengubah Daftar di HList yang menjadi alasan mengapa Anda perlu meneruskan informasi tipe ke
toHList
metodesumber
Shapeless 2.0 mengubah beberapa sintaks. Inilah solusi yang diperbarui menggunakan tidak berbentuk.
import shapeless._ import HList._ import syntax.std.traversable._ val x = List(1, 2, 3) val y = x.toHList[Int::Int::Int::HNil] val z = y.get.tupled
Masalah utamanya adalah tipe untuk .toHList harus ditentukan sebelumnya. Secara lebih umum, karena tupel terbatas dalam aritasnya, desain perangkat lunak Anda mungkin lebih baik disajikan dengan solusi yang berbeda.
Namun, jika Anda membuat daftar secara statis, pertimbangkan solusi seperti ini, juga menggunakan tak berbentuk. Di sini, kami membuat HList secara langsung dan jenisnya tersedia pada waktu kompilasi. Ingatlah bahwa HList memiliki fitur dari tipe List dan Tuple. yaitu dapat memiliki elemen dengan tipe berbeda seperti Tuple dan dapat dipetakan di antara operasi lain seperti koleksi standar. HLists membutuhkan sedikit waktu untuk membiasakan diri, jadi melangkahlah perlahan jika Anda baru.
scala> import shapeless._ import shapeless._ scala> import HList._ import HList._ scala> val hlist = "z" :: 6 :: "b" :: true :: HNil hlist: shapeless.::[String,shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.HNil]]]] = z :: 6 :: b :: true :: HNil scala> val tup = hlist.tupled tup: (String, Int, String, Boolean) = (z,6,b,true) scala> tup res0: (String, Int, String, Boolean) = (z,6,b,true)
sumber
Terlepas dari kesederhanaan dan tidak untuk daftar dengan panjang apa pun, itu aman untuk tipe dan jawabannya dalam banyak kasus:
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))
Kemungkinan lain, ketika Anda tidak ingin menamai daftar atau mengulanginya (saya harap seseorang dapat menunjukkan cara untuk menghindari Seq / bagian kepala):
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
sumber
FWIW, saya ingin tupel untuk menginisialisasi sejumlah bidang dan ingin menggunakan gula sintaksis dari tugas tupel. MISALNYA:
val (c1, c2, c3) = listToTuple(myList)
Ternyata ada gula sintaksis untuk menugaskan konten daftar juga ...
val c1 :: c2 :: c3 :: Nil = myList
Jadi tidak perlu tupel jika Anda mengalami masalah yang sama.
sumber
val List(c1, c2, c3) = myList
Anda tidak dapat melakukan ini dengan cara yang aman. Di Scala, daftar adalah urutan elemen dengan panjang arbitrer dari beberapa jenis. Sejauh yang diketahui sistem tipe,
x
bisa menjadi daftar panjang sewenang-wenang.Sebaliknya, arity dari sebuah tupel harus diketahui pada waktu kompilasi. Ini akan melanggar jaminan keamanan dari sistem tipe untuk memungkinkan penetapan
x
ke tipe tupel.Faktanya, karena alasan teknis, tupel Scala dibatasi hingga 22 elemen , tetapi batas tersebut tidak lagi ada di 2.11 Batas kelas kasus telah dicabut di 2.11 https://github.com/scala/scala/pull/2305
Dimungkinkan untuk mengkodekan fungsi secara manual yang mengonversi daftar hingga 22 elemen, dan memunculkan pengecualian untuk daftar yang lebih besar. Dukungan template Scala, fitur yang akan datang, akan membuatnya lebih ringkas. Tapi ini akan menjadi peretasan yang buruk.
sumber
Jika Anda sangat yakin bahwa list.size Anda <23 gunakan:
def listToTuple[A <: Object](list:List[A]):Product = { val class = Class.forName("scala.Tuple" + list.size) class.getConstructors.apply(0).newInstance(list:_*).asInstanceOf[Product] } listToTuple: [A <: java.lang.Object](list: List[A])Product scala> listToTuple(List("Scala", "Smart")) res15: Product = (Scala,Smart)
sumber
Ini juga dapat dilakukan
shapeless
dengan lebih sedikit boilerplate menggunakanSized
:scala> import shapeless._ scala> import shapeless.syntax.sized._ scala> val x = List(1, 2, 3) x: List[Int] = List(1, 2, 3) scala> x.sized(3).map(_.tupled) res1: Option[(Int, Int, Int)] = Some((1,2,3))
Ini jenis-aman: Anda mendapatkan
None
, jika ukuran tupel salah, tetapi ukuran tupel harus literal ataufinal val
(dapat diubahshapeless.Nat
).sumber
Menggunakan Pencocokan Pola:
val intTuple = List(1,2,3) match {case List(a, b, c) => (a, b, c)}
sumber
List
/Tuple
adalah konstanta yang diketahui.2015 pos. Agar jawaban Tom Crockett lebih memperjelas , berikut adalah contoh nyata.
Awalnya saya bingung. Karena saya berasal dari Python, di mana Anda bisa melakukannya
tuple(list(1,2,3))
.Apakah ini pendek dari bahasa Scala? (jawabannya adalah - ini bukan tentang Scala atau Python, ini tentang tipe statis dan tipe dinamis.)
Itulah yang menyebabkan saya mencoba menemukan inti mengapa Scala tidak dapat melakukan ini.
Contoh kode berikut mengimplementasikan sebuah
toTuple
metode, yang memiliki tipe-amantoTupleN
dan tipe-tidak amantoTuple
.The
toTuple
Metode mendapatkan informasi jenis-panjang pada saat run-time, yaitu tidak ada informasi jenis-panjang pada saat kompilasi, sehingga jenis kembali adalahProduct
yang sangat seperti Pythontuple
memang (ada jenis pada setiap posisi, dan tidak ada panjang jenis).Cara itu rentan terhadap error runtime seperti type-mismatch atau
IndexOutOfBoundException
. (jadi list-to-tuple Python yang nyaman bukanlah makan siang gratis.)Sebaliknya, panjang informasi yang disediakan pengguna yang membuat
toTupleN
waktu kompilasi aman.implicit class EnrichedWithToTuple[A](elements: Seq[A]) { def toTuple: Product = elements.length match { case 2 => toTuple2 case 3 => toTuple3 } def toTuple2 = elements match {case Seq(a, b) => (a, b) } def toTuple3 = elements match {case Seq(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!
sumber
kamu juga bisa melakukan ini
dengan mengulangi daftar dan menerapkan setiap elemen satu per satu.
val xs: Seq[Any] = List(1:Int, 2.0:Double, "3":String) val t: (Int,Double,String) = xs.foldLeft((Tuple3[Int,Double,String] _).curried:Any)({ case (f,x) => f.asInstanceOf[Any=>Any](x) }).asInstanceOf[(Int,Double,String)]
sumber
sejauh Anda memiliki tipe:
val x: List[Int] = List(1, 2, 3) def doSomething(a:Int *) doSomething(x:_*)
sumber