Dato il seguente elenco:Esiste un modo sicuro in Scala per trasporre un elenco di elenchi di lunghezza non uguale?
val l = List(List(1, 2, 3), List(4, 5), List(6, 7, 8))
Se cerco di trasposizione, Scala sarà buttare il seguente errore:
scala> List.transpose(l)
java.util.NoSuchElementException: head of empty list
at scala.Nil$.head(List.scala:1365)
at scala.Nil$.head(List.scala:1362)
at scala.List$$anonfun$transpose$1.apply(List.scala:417)
at scala.List$$anonfun$transpose$1.apply(List.scala:417)
at scala.List.map(List.scala:812)
at scala.List$.transpose(List.scala:417)
at .<init>(<console>:6)
at .<clinit>(<console>)
at RequestResult...
Questo perché List.transpose
assume liste di uguale lunghezza e quindi utilizza il metodo head
:
def transpose[A](xss: List[List[A]]): List[List[A]] = {
val buf = new ListBuffer[List[A]]
var yss = xss
while (!yss.head.isEmpty) {
buf += (yss map (_.head))
yss = (yss map (_.tail))
}
buf.toList
}
Vorrei ottenere le seguenti:
01.235.164,106 milaList(List(1, 4, 6), List(2, 5, 7), List(3, 8))
Sta scrivendo la mia versione di transpose
il modo migliore per farlo? Questo è ciò che mi si avvicinò con:
def myTranspose[A](xss: List[List[A]]): List[List[A]] = {
val buf = new ListBuffer[List[A]]
var yss = xss
while (!yss.head.isEmpty) {
buf += (yss filter (!_.isEmpty) map (_.head))
yss = (yss filter (!_.isEmpty) map (_.tail))
}
buf.toList
}
Aggiornamento: ero interessato a confrontare la velocità delle diverse soluzioni proposte qui, così ho messo insieme il seguente piccolo punto di riferimento:
import scala.testing.Benchmark
import scala.collection.mutable.ListBuffer
trait Transpose extends Benchmark {
def transpose[Int](xss: List[List[Int]]): List[List[Int]] = Nil
val list: List[List[Int]] = List(List(1,2,3), Nil, List(4,5,99,100), List(6,7,8))
def run = {
val l = transpose(list)
println(l)
l
}
}
object PRTranspose extends Transpose {
override def transpose[Int](xss: List[List[Int]]): List[List[Int]] = {
val buf = new ListBuffer[List[Int]]
var yss = xss
while (!yss.head.isEmpty) {
buf += (yss filter (!_.isEmpty) map (_.head))
yss = (yss filter (!_.isEmpty) map (_.tail))
}
buf.toList
}
}
object ACTranspose extends Transpose {
override def transpose[Int](xss: List[List[Int]]): List[List[Int]] = {
val b = new ListBuffer[List[Int]]
var y = xss filter (!_.isEmpty)
while (!y.isEmpty) {
b += y map (_.head)
y = y map (_.tail) filter (!_.isEmpty)
}
b.toList
}
}
object ETranspose extends Transpose {
override def transpose[Int](xss: List[List[Int]]): List[List[Int]] = xss.filter(!_.isEmpty) match {
case Nil => Nil
case ys: List[List[Int]] => ys.map{ _.head }::transpose(ys.map{ _.tail })
}
}
mio i comandi sono stati:
scala PFTranspose 5 out.log
scala ACTranspose 5 out.log
scala ETranspose 5 out.log
I miei risultati sono stati:
PRTranspose$ 10 0 1 1 0
ACTranspose$ 9 2 0 0 0
ETranspose$ 9 3 2 3 1
Si intende gestire il caso in cui il primo elenco (Elenco (1,2,3)) dell'input non è la dimensione massima di tutti gli elenchi. Per esempio. come gestisci l'inserimento dell'elenco (Elenco (1,2,3), Elenco (4,5,99,100), Elenco (6,7,8))? –
FWIW, Scala 2.8 non ha questo bug. –
Ma ha un bug se il primo elenco non è almeno così grande come nessun altro. –