如何在scala中创建连续的正或负值列表

时间:2019-12-02 18:26:54

标签: scala data-structures

列出scala中正数或负数的连续值

val a = List(1,1,2,3,-1,2,3,4,-1,-2,-3)  //input
val z = List(List(1,2,3),List(-1),List(2,3,4),List(-1,-2,-3))  //expected

我尝试使用以下代码来显示列表。

val sd = a.span(_>=0) match {
  case (left, Nil)   => left
  case (left, right) => left ++ ( right.filter(_>=0)) 
}

2 个答案:

答案 0 :(得分:2)

恕我直言,始终使用 tail-recursive 算法来操纵 List 的最佳方法。

sealed trait Sign

object Sign {
  final case object Positive extends Sign
  final case object Negative extends Sign
  final case object Neutral extends Sign

  def of(i: Int): Sign =
    if (i > 0) Positive
    else if (i < 0) Negative
    else Neutral
}

def extractBySign(sign: Sign)(list: List[Int]): (List[Int], List[Int]) = {
  @annotation.tailrec
  def loop(remaining: List[Int], acc: List[Int]): (List[Int], List[Int]) =
    remaining match {
      case Nil =>
        acc.reverse -> Nil

      case x :: xs if (Sign.of(x) == sign) =>
        loop(remaining = xs, x :: acc)

      case list =>
        acc.reverse -> list
    }

  loop(remaining = list, acc = List.empty)
}

def splitBySign(list: List[Int]): List[List[Int]] = {
  @annotation.tailrec
  def loop(remaining: List[Int], acc: List[List[Int]]): List[List[Int]] =
    remaining match {
      case x :: xs =>
        val sign = Sign.of(x)  
        val (list, newRemaining) = extractBySign(sign)(x :: xs)
        loop(newRemaining, list :: acc)   

      case Nil =>
        acc.reverse
    }

  loop(remaining = list, acc = List.empty)
}

答案 1 :(得分:2)

这是foldLeft

的解决方案
def consecutiveLists(as : List[Int]) : List[List[Int]] =
  as.foldLeft(List[List[Int]](List[Int]())) ((a,b) => {
    val index = a.size - 1
    val currentList = a.last

    if(b >= 0 && (currentList.isEmpty || currentList.last < 0)) {
      a :+ List(b)
    } else if(b >=0 && (currentList.isEmpty || currentList.last >= 0)) {
      a.updated(index, currentList :+ b)
    } else if(b < 0 && (currentList.isEmpty || currentList.last >=0)) {
      a :+ List(b)
    } else {
      a.updated(index, currentList :+ b)
    }
  })

测试:

consecutiveLists(List(1,1,2,3,-1,2,3,4,-1,-2,-3))
consecutiveLists(List(-1,-2,1,1,2,3,-1,2,3,4,-1,-2,-3))
consecutiveLists(List(-1))
consecutiveLists(List(-1,1,-1,1,2,-1,-1))

res0: List[List[Int]] = List(List(), List(1, 1, 2, 3), List(-1), List(2, 3, 4), List(-1, -2, -3))
res1: List[List[Int]] = List(List(), List(-1, -2), List(1, 1, 2, 3), List(-1), List(2, 3, 4), List(-1, -2, -3))
res2: List[List[Int]] = List(List(), List(-1))
res3: List[List[Int]] = List(List(), List(-1), List(1), List(-1), List(1, 2), List(-1, -1))

注意:

  • 结果将始终有一个空列表作为保护值。 drop(1)可以摆脱这个问题

  • 这四个条件可以简化为两个。我没有进一步简化它以显示基本逻辑:

  

如果当前值与符号相反,请查看最后一个列表   最后一个列表,创建新列表,否则只需将当前值附加到最后一个列表。有   可能还有其他方法来简化它。

  • 在这种情况下,List[List[Int]]可能不是最佳的数据结构。 Array[List[Int]]Vector[List[Int]]可能是更好的选择。

编辑:这是简化版:

as.foldLeft(List[List[Int]](List[Int]())) ((a,b) => {
    val index = a.size - 1
    val currentList = a.last

    if((b >= 0 && (currentList.isEmpty || currentList.last < 0)) ||
      (b < 0 && (currentList.isEmpty || currentList.last >=0))) {
      a :+ List(b)
    } else {
      a.updated(index, currentList :+ b)
    }
  })

编辑2 :这是另一种简化方法。我正在使用保证O(1)添加项目的队列。对于保留列表,我正在使用矢量,因为此列表既可以更新也可以插入。另外,通过确保fold entry参数从不为空,也删除了对空列表的检查。

import scala.collection.immutable.Queue
type QI = Queue[Int]

def consecutiveLists(as : List[Int]) : Vector[QI] =
  if(as.isEmpty) 
    Vector.empty[QI]
  else
    as.drop(1).foldLeft(Vector[QI](Queue[Int](as.head))) ((a,b) => {
      val currentList = a.last

      if((b >= 0 && currentList.last < 0) ||
        (b < 0 && currentList.last >=0)) {
        a :+ Queue(b)
      } else {
        a.updated(a.size - 1, currentList :+ b)
      }
    })