Scala listesi bir demete dönüştürülsün mü?


89

3 öğeli bir listeyi 3 boyutlu bir demete nasıl dönüştürebilirim?

Örneğin, diyelim ki ben var val x = List(1, 2, 3)ve bunu (1, 2, 3). Bunu nasıl yapabilirim?


1
Mümkün değil ("elle" hariç) AFAIK. Verilirse def toTuple(x: List[Int]): R, R türü ne olmalıdır?

7
Keyfi boyutlu bir demet için yapılması gerekmiyorsa (yani yukarıda sunulan varsayımsal bir yöntem gibi), x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }sonuç türünün sabit olduğunu göz önünde bulundurun ve not edinTuple3[Int,Int,Int]

2
Yapmak istediğiniz şey mümkün olmasa da List, demetlereHList ve demetlerden dönüşüme izin veren Shapeless ' türüne bakabilirsiniz ( github.com/milessabin/… ). Belki kullanım durumunuz için geçerlidir.

Yanıtlar:


59

Bunu tip güvenli bir şekilde yapamazsınız. Neden? Çünkü genel olarak bir listenin uzunluğunu çalışma zamanına kadar bilemeyiz. Ancak bir demetin "uzunluğu" kendi türünde kodlanmalı ve dolayısıyla derleme zamanında bilinmelidir. Örneğin şeker (1,'a',true)türü vardır . Tupleların bu kısıtlamaya sahip olmasının nedeni, homojen olmayan türleri işleyebilmeleri gerektiğidir.(Int, Char, Boolean)Tuple3[Int, Char, Boolean]


Aynı türe sahip bazı sabit boyutlu öğeler listesi var mı? Elbette şekilsiz gibi standart olmayan kitaplıkları içe aktarmamak.

1
@davips, bildiğim kadarıyla değil, ancak kendinizinkini yapmak yeterince kolay, örneğincase class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett

Bu, aynı tipteki elemanların bir "demeti" olabilir, örneğin üzerine harita uygulayamam.

1
@davips, her yeni veri türünde olduğu gibi, etc'nin bunun için nasıl mapçalıştığını tanımlamanız gerekir
Tom Crockett

1
Bu tür bir sınırlama, her şeyden çok tip güvenliğinin yararsızlığının yankılanan bir göstergesi gibi görünüyor. Bana "boş küme birçok ilginç özelliğe sahiptir" sözünü hatırlatıyor.
ely

58

Bunu, ölçek çıkarıcıları ve desen eşleştirmesi ( bağlantı ) kullanarak yapabilirsiniz :

val x = List(1, 2, 3)

val t = x match {
  case List(a, b, c) => (a, b, c)
}

Bir demet veren

t: (Int, Int, Int) = (1,2,3)

Ayrıca, Listenin boyutundan emin değilseniz bir joker karakter operatörü kullanabilirsiniz.

val t = x match {
  case List(a, b, c, _*) => (a, b, c)
}

4
Bu çözüm bağlamında, tür güvenliği ile ilgili şikayetler, çalışma zamanında bir MatchError alabileceğiniz anlamına gelir. İsterseniz, bu hatayı yakalamayı deneyebilir ve Listenin uzunluğu yanlışsa bir şeyler yapabilirsiniz. Bu çözümün bir başka güzel özelliği de çıktının bir demet olması gerekmemesidir, kendi özel sınıflarınızdan biri veya sayıların bazı dönüşümü olabilir. Yine de bunu Listeler olmayanlarla yapabileceğinizi sanmıyorum (bu nedenle girdiye bir .toList işlemi yapmanız gerekebilir).
Jim Pivarski

Bunu, +: sözdizimini kullanarak herhangi bir Scala dizisi ile yapabilirsiniz. Ayrıca, hepsini yakalama eklemeyi de unutmayın
ig-dev

48

şekilsiz kullanan bir örnek :

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

Not: derleyicinin HList'teki Listeyi dönüştürmek için bazı tür bilgilerine ihtiyacı vardır, neden tür bilgilerini toHListyönteme iletmeniz gerekir


19

Shapeless 2.0 bazı sözdizimini değiştirdi. İşte şekilsiz kullanan güncellenmiş çözüm.

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

Ana sorun, .toHList türünün önceden belirtilmesi gerektiğidir. Daha genel olarak, demetler sınırlı oldukları için, yazılımınızın tasarımı farklı bir çözümle daha iyi sunulabilir.

Yine de, statik olarak bir liste oluşturuyorsanız, bunun gibi, şekilsiz kullanarak da bir çözüm düşünün. Burada, doğrudan bir HList oluşturuyoruz ve tür, derleme zamanında kullanılabilir. Bir HList'in hem List hem de Tuple türlerinden özelliklere sahip olduğunu unutmayın. yani, bir Tuple gibi farklı türlerde öğelere sahip olabilir ve standart koleksiyonlar gibi diğer işlemler arasında eşleştirilebilir. HListlerin alışması biraz zaman alır, ancak yeniyseniz yavaş ilerleyin.

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)

13

Sadeliğine ve herhangi bir uzunluktaki listeler için olmamasına rağmen, tür açısından güvenlidir ve çoğu durumda cevaptır:

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))

Listeyi adlandırmak veya tekrarlamak istemediğinizde başka bir olasılık (umarım birisi Seq / head bölümlerinden kaçınmanın bir yolunu gösterebilir):

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

10

FWIW, birkaç alanı ilk kullanıma sokacak bir demet istedim ve demet atamasının sözdizimsel şekerini kullanmak istedim. ÖRNEĞİN:

val (c1, c2, c3) = listToTuple(myList)

Bir listenin içeriğini atamak için de sözdizimsel şeker olduğu ortaya çıktı ...

val c1 :: c2 :: c3 :: Nil = myList

Yani aynı sorunu yaşıyorsanız demetlere gerek yok.


@javadba listToTuple () 'ı nasıl uygulayacağımı arıyordum ama buna gerek kalmadı. Sözdizimsel şeker listesi sorunumu çözdü.
Peter L

Bence biraz daha iyi görünen başka bir sözdizimi daha var:val List(c1, c2, c3) = myList
Kolmar

7

Bunu tür güvenli bir şekilde yapamazsınız. Scala'da listeler, bazı türlerdeki öğelerin keyfi uzunlukta dizileridir. Tip sisteminin bildiği kadarıyla x, keyfi uzunlukların bir listesi olabilir.

Aksine, bir demetinin uyuşması derleme zamanında bilinmelidir. xBir demet tipine atamaya izin vermek için tip sisteminin güvenlik garantilerini ihlal eder .

Aslında, teknik nedenlerden ötürü, Scala kayıtları 22 öğe ile sınırlıydı , ancak sınır artık 2.11'de mevcut değil. Vaka sınıfı sınırı 2.11 https://github.com/scala/scala/pull/2305'te kaldırılmıştır.

22 öğeye kadar listeleri dönüştüren ve daha büyük listeler için bir istisna oluşturan bir işlevi manuel olarak kodlamak mümkün olabilir. Scala'nın gelecek bir özellik olan şablon desteği, bunu daha kısa hale getirecektir. Ama bu çirkin bir hack olurdu.


Bu varsayımsal işlevin ortaya çıkan türü Herhangi biri olur mu?

Vaka sınıfı sınırı 2.11 github.com/scala/scala/pull/2305'te
gdoubleod

5

List.size <23 olduğundan çok eminsen kullan:

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)

5

Bu, aşağıdakiler shapelesskullanılarak daha az standart şablonla da yapılabilir Sized:

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))

Tür açısından güvenlidir: Nonetuple boyutu yanlışsa, ancak tuple boyutunun değişmez veya final val(dönüştürülebilmesi için shapeless.Nat) olması gerekir .


Bu, 22'den büyük bedenler için, en azından şekilsiz_2.11: 2.3.3 için işe yaramıyor gibi görünüyor
kfkhalili

@kfkhalili Evet ve şekilsiz bir sınırlama değil. Scala 2'nin kendisi 22'den fazla öğeye sahip tuple'lara izin vermez, bu nedenle daha büyük boyutlu bir Listeyi herhangi bir yöntem kullanarak Tuple'a dönüştürmek mümkün değildir.
Kolmar

4

Desen Eşleştirmeyi Kullanma:

val intTuple = List(1,2,3) match {case List(a, b, c) => (a, b, c)}

Bu kadar eski (6 yıldan fazla) bir soruyu cevaplarken, cevabınızın daha önce sunulmuş olan tüm (12) eski cevaplardan ne kadar farklı olduğunu belirtmek genellikle iyi bir fikirdir. Özellikle, cevabınız yalnızca List/ uzunluğu Tuplebilinen bir sabitse uygulanabilir.
jwvh

Teşekkürler @ jwvh, yorumlarınızı ileriye dönük olarak değerlendireceğiz.
Michael Olafisoye

2

2015 yazısı. For Tom Crockett cevabı daha edilmesi netleştirilmesi , burada gerçek bir örnektir.

İlk başta kafam karıştı. Çünkü ben yapabileceğin Python'dan geliyorum tuple(list(1,2,3)).
Scala dilinin kısaltması mı? (cevap - Scala veya Python ile ilgili değil, statik tip ve dinamik tip ile ilgili.)

Bu, Scala'nın bunu neden yapamayacağını bulmaya çalışmama neden oluyor.


Aşağıdaki kod örneği toTuple, tür açısından güvenli toTupleNve güvenli olmayan türlere sahip bir yöntemi uygular toTuple.

toTupleDönüş türü yani yöntem olup, çalışma zamanında tip uzunluğa, derleme zamanında, yani herhangi bir tür uzunlukta bilgi almak Productçok Python'ın gibi olan tuplegerçekten (her pozisyonda bir tip ve türde bir uzunluğu).
Bu yol, tür uyuşmazlığı veya IndexOutOfBoundException. (bu yüzden Python'un uygun liste başı ücretsiz öğle yemeği değildir.)

Aksine, toTupleNderleme zamanını güvenli kılan, kullanıcının sağladığı uzunluk bilgisidir .

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!

Güzel görünüyor - şimdi deniyor
StephenBoesch

2

bunu da yapabilirsin

  1. kalıp eşleştirme yoluyla (ne istemiyorsanız) veya
  2. Listede yineleyerek ve her öğeyi tek tek uygulayarak.

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

1

türüne sahip olduğunuz sürece:

val x: List[Int] = List(1, 2, 3)

def doSomething(a:Int *)

doSomething(x:_*)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.