计算列表中的连续字符?

时间:2019-09-26 20:14:45

标签: scala recursion

我想创建一个scala方法,该方法计算值相同的连续字符的数量。所以我有这个清单:

List('a','a','b')

,我想返回类似List(('a',2),'b',1)之类的东西-因为两个字符之间的值相同。我对此一无所获,但收效甚微:

  def recursivelyCompressList(list: List[(Char, Int)], newString: List[(Char, Int)]): List[(Char, Int)] = {

    list match {
      case Nil => newString
      case s :: tail => {
        if (tail.nonEmpty && s._1 == tail.head._1) {
          recursivelyCompressList(tail, newString :+ (s._1, s._2 + 1))
        } else {
          recursivelyCompressList(tail, newString :+ s)
        }

      }
      case _ => newString

    }

  }

感谢任何指导。

4 个答案:

答案 0 :(得分:1)

还可以使用span代替dropWhile和takeWhile以避免双重扫描

def comp(xs:List[Char]):List[(Char,Int)] =
    if(xs.isEmpty) Nil
    else {
      val h = xs.head
      val (m,r) = xs.span(_ == h)
      (h, m.length) :: comp(r)
    }

答案 1 :(得分:0)

这应该有效。
我希望代码会自我解释,但是如果您有任何疑问,请不要怀疑。

def compressList[T](list: List[T]): List[(T, Int)] = {
  @annotation.tailrec
  def loop(remaining: List[T], currentValue: T, currentCount: Int, acc: List[(T, Int)]): List[(T, Int)] =
    remaining match {
      case Nil =>
        ((currentValue -> currentCount) :: acc).reverse

      case t :: tail =>
        if (t == currentValue)
          loop(
            remaining = tail,
            currentValue,
            currentCount + 1,
            acc
          )
        else
          loop(
            remaining = tail,
            currentValue = t,
            currentCount = 1,
            (currentValue -> currentCount) :: acc
          )
    }

  list match {
    case Nil =>
      Nil

    case t :: tail =>
      loop(
        remaining = tail,
        currentValue = t,
        currentCount = 1,
        acc = List.empty
      )
  }
}

您可以这样使用:

compressList(List.empty[Char])
// res: List[(Char, Int)] = List()

compressList(List('a', 'b'))
// res: List[(Char, Int)] = List(('a', 1), ('b', 1))

compressList(List('a', 'a', 'b'))
// res: List[(Char, Int)] = List(('a', 2), ('b', 1))

compressList(List('a', 'a', 'b', 'b', 'b', 'a', 'c'))
// res: List[(Char, Int)] = List(('a', 2), ('b', 3), ('a', 1), ('c', 1))

答案 2 :(得分:0)

使用takeWhiledropWhile

连续计数

def count(xs: List[Char]): List[(Char, Int)] =
if (xs.isEmpty) Nil else
 (xs.head, xs.takeWhile(_ == xs.head).length) :: count(xs.dropWhile(_ == xs.head))

Scala REPL

scala> def count(xs: List[Char]): List[(Char, Int)] =
     | if (xs.isEmpty) Nil else
     |  (xs.head, xs.takeWhile(_ == xs.head).length) :: count(xs.dropWhile(_ == xs.head))
count: (xs: List[Char])List[(Char, Int)]

scala> count(List('a', 'a', 'b', 'c', 'c', 'c'))
res0: List[(Char, Int)] = List((a,2), (b,1), (c,3))

scala> count(List('a', 'a', 'b', 'a', 'c', 'c', 'c'))
res1: List[(Char, Int)] = List((a,2), (b,1), (a,1), (c,3))

答案 3 :(得分:0)

如果存在多个相同字符的重复序列,则尚未指定所需的行为。假设您只想要最长的重复序列,那么以下代码将是一个很好的起点:

def rec(list : List[Char]) : Map[Char, Int] = {

  @scala.annotation.tailrec
  def helper(prev: Char, li : List[Char], len : Int, result : Map[Char, Int]) : Map[Char,Int] = {
    if(li.isEmpty) {
      if(!result.contains(prev)) result + (prev -> len)
      else if(result(prev) < len) result + (prev -> len)
      else result
    }
    else {
      val cur = li.head
      if(cur != prev) {
        if(result.contains(prev)) {
          if(result(prev) < len)
            helper(li.head, li.tail, 1, result + (prev -> len))
          else
            helper(li.head, li.tail, 1, result)
        } else {
          helper(li.head, li.tail, 1, result + (prev -> len))
        }
      } else {
        helper(li.head, li.tail, len + 1, result)
      }
    }
  }
  helper('\0', list, 0, Map.empty[Char, Int]) - '\0'
}

运行

rec(List('c', 'a', 'a', 'a', 'c', 'd' ,'c', 'c' , 'a', 'a', 'd', 'd', 'd', 'd','c','c','a', 'c','c','c'))

输出:

res0: Map[Char,Int] = Map(c -> 3, a -> 3, d -> 4)

这个想法只是看列表中的当前字符和前一个字符。当字符更改时,序列计数将停止,并将当前长度与映射中存储的长度进行比较。当您考虑它时,这很简单。

我认为这可以写得更加优雅。但这可能是一个很好的起点。