Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 嵌套集合的zipWithIndex(无可变状态的发布)_Scala - Fatal编程技术网

Scala 嵌套集合的zipWithIndex(无可变状态的发布)

Scala 嵌套集合的zipWithIndex(无可变状态的发布),scala,Scala,有一些嵌套集合: val xs = List( List("a","b"), List("c"), List("d", "e", "f")) 我想为嵌套列表的元素创建唯一索引: val result = List( List(("a", 0), ("b", 1)), List(("c", 2)), List(("d", 3), ("e", 4), ("f", 5)))

有一些嵌套集合:

val xs = List(
           List("a","b"), 
           List("c"), 
           List("d", "e", "f"))
我想为嵌套列表的元素创建唯一索引:

val result = List(
         List(("a", 0), ("b", 1)), 
         List(("c", 2)),
         List(("d", 3), ("e", 4), ("f", 5)))
这是一个糟糕的解决方案(使用可变状态):


如何在没有可变状态的情况下释放此任务?

这就是我想到的:

def iterate(someList: List[List[Int]], counter: Int = 0, acc: List[List[(Int, Int)]] = List()): List[List[(Int, Int)]] = someList match {
  case head :: tail =>
    val newCounter = counter + head.length
    val sublist = head.zipWithIndex.map {
      // add the counter to the index
      case (v, i) => (v, i + counter)
    }
    // recurse
    iterate(tail, newCounter, acc :+ sublist)
  case _ => acc
}

scala> iterate(List(List(1,2), List(3,4)))
res3: List[List[(Int, Int)]] = List(List((1,0), (2,1)), List((3,2), (4,3)))

scala> iterate(List(List(1,2), List(3,4), List(5)))
res4: List[List[(Int, Int)]] = List(List((1,0), (2,1)), List((3,2), (4,3)), List((5,4)))
这基本上是迭代一个列表列表,使用每个子列表的索引压缩,并添加一个计数器值,该计数器值考虑了所有以前的列表长度,当列表为空时,我们返回一个累加器


但正如我所说的,我不会用可变版本来交换它。

您可以始终应用足够的蛮力将命令while循环转换为功能性的
foldLeft

val xs = List(List("a", "b"), List("c"), List("d", "e", "f"))

def addIndeces(xs: List[List[String]]): List[List[(String, Int)]] = {
  val outerLoopState = 0 -> Vector.empty[List[(String, Int)]]

  val (finalCounter, finalResult) = xs.foldLeft(outerLoopState) {
    case ((counter, result), sublist) =>
      val innerLoopState = counter -> Vector.empty[(String, Int)]
      val (newCounter, subResult) = sublist.foldLeft(innerLoopState) {
        case ((counter, subResult), element) =>
          (counter + 1, subResult :+ (element, counter))
      }

      (newCounter, result :+ subResult.toList)
  }

  finalResult.toList
}

// scala> addIndeces(xs)
// res0: List[List[(String, Int)]] = List(List((a,0), (b,1)), List((c,2)), List((d,3), (e,4), (f,5)))
我使用了
Vector
作为中间结果,以获得更有效的
append
函数操作。如果使用
列表
,我将不得不预先设置中间结果,然后反转中间结果。

还有另一种变化:

val index = Iterator.from(0)
for (sl <- xs) yield for (e <- sl) yield (e, index.next)
解决方案1(使用
折叠
):

解决方案2(使用递归)

解决方案3:

scala> (xs, xs.map(_.size).scanLeft(0){ _+_ }).zipped map { (a, b) => a.zip(Stream.from(b)) }
res3: List[List[(String, Int)]] = List(List((a,0), (b,1)), List((c,2)), List((d,3), (e,4), (f,5)))

它工作快速简单

简单无尾递归

 def zipFrom[A](start:Int)(l:List[A]):(List[(A,Int)],Int) = {
  val end = start + l.length
  val ys = l zip (start to end)
  ys -> end
 }

 def zp[A](xs:List[List[A]],acc:Int):List[List[(A,Int)]] = xs match {
  case Nil    => Nil
  case h :: t =>
   val (l,e) = zipFrom(acc)(h)
   l :: zp(t,e)
  }

使用map而不是for对@原型Paul进行变异

val ys = Iterator.from(0)
val zs = xs.map { _.map ((_,ys.next)) }
使用zip、迭代器和map的变体

val ys = xs.flatten.zipWithIndex.iterator
val zs = xs.map { _.map ( x=> ys.next) }

请注意,使用可变状态并不是一个坏的解决方案,有些情况下,您确实希望使用可变状态,因为它们使您的代码更易读,在这种情况下,我肯定会使用可变状态,它直观、简单,并且您不会创建太多变量或中间集合。无论如何,出于好奇,我也想看到一个纯功能性的解决方案。事实上,如果我们看看zipWithIndex的来源,我们会看到。。一个可变的当前索引!这很好。你可以通过在第一个for循环中嵌入第二个for循环来缩短它:
for(sl@IonutGStan,不,你不能。这最终会使列表变平。#1是我的第一个想法。#3是很好的预计算b,对眼睛来说很容易。效率较低的变体,但对眼睛和大脑来说很容易:
xs.zipWithIndex映射{case(xx,i)=>xx zip(来自xs.take(i).map(u.size.sum)的流)
与@Eastsun.相比,这是一个很大的LOC(ALOLOC?)。难道不是
acc:+(foo)
语法只在模糊的编译器调试输出中使用吗?
acc:+子列表作为一行:
def-zipdeep[a](xx:List[a]],count:Int=0,res:List[List[(a,Int]=Nil):List[List[(a,Int)]=List[(a,Int)]=匹配]{case a::b=>zipdeep(b,count+a.size,res:+(a.zipWithIndex map{case(v,i)=>(v,i+count)})case=>res}
奇怪的是,我有时使用这个点(比如
:+
),有时不使用(比如
+
),至于单行程序,我在第一个答案中有类似的内容,然后我将所有代码移到变量中以使其更具可读性。是的,这很奇怪。我只尝试了单行程序,看看它是否显示了递归有多整洁。奥德斯基说折叠通常作为递归函数更简单。那里有一些冗余位。
case l@List(*)=>
可以是
l=>
,不需要范围的
.toList
scala> (xs, xs.map(_.size).scanLeft(0){ _+_ }).zipped map { (a, b) => a.zip(Stream.from(b)) }
res3: List[List[(String, Int)]] = List(List((a,0), (b,1)), List((c,2)), List((d,3), (e,4), (f,5)))
var index = 0
val lim = li.map {
  case l @ List(_*) =>
    val s = index
    index += l.length
    l.zip(s until index)
}
 def zipFrom[A](start:Int)(l:List[A]):(List[(A,Int)],Int) = {
  val end = start + l.length
  val ys = l zip (start to end)
  ys -> end
 }

 def zp[A](xs:List[List[A]],acc:Int):List[List[(A,Int)]] = xs match {
  case Nil    => Nil
  case h :: t =>
   val (l,e) = zipFrom(acc)(h)
   l :: zp(t,e)
  }
val ys = Iterator.from(0)
val zs = xs.map { _.map ((_,ys.next)) }
val ys = xs.flatten.zipWithIndex.iterator
val zs = xs.map { _.map ( x=> ys.next) }