如何在scala中连续和非连续列表?

How to consecutive and non-consecutive list in scala?

val keywords = List("do", "abstract","if")

val resMap = io.Source
               .fromFile("src/demo/keyWord.txt")
               .getLines()
               .zipWithIndex
       .foldLeft(Map.empty[String,Seq[Int]].withDefaultValue(Seq.empty[Int])){
         case (m, (line, idx)) =>
           val subMap = line.split("\W+")
                            .toSeq  //separate the words
                            .filter(keywords.contains)  //keep only key words
                            .groupBy(identity)  //make a Map w/ keyword as key
                            .mapValues(_.map(_ => idx+1))  //and List of line numbers as value
                            .withDefaultValue(Seq.empty[Int])
           keywords.map(kw => (kw, m(kw) ++ subMap(kw))).toMap
       }

println("keyword\t\tlines\t\tcount")
keywords.sorted.foreach{kw =>
  println(kw + "\t\t" +
          resMap(kw).distinct.mkString("[",",","]") + "\t\t" +
          resMap(kw).length)
}

此代码不是我的,也不属于我...用于学习目的。但是,我仍在学习并且我坚持实现连续到非连续列表,例如 "if" 这个词在多行中,当出现三个或更多连续行号时,它们之间应该写一个破折号,例如20-22,但不是 20、21、22。我该如何实施?我就是想学这个。

输出:

keyword     lines       count
abstract    [1]         1
do          [6]         1
if      [14,15,16,17,18]    5

但我希望结果是 [14-18] 因为单词 "if" 在第 14 到 18 行。

首先,我会按照惯例给出警告,即 SO 并不是一个众包家庭作业或项目答案的地方。我会让你相信事实并非如此。

也就是说,我希望你能从这个建议中获得一些关于分解这个问题的理解:

  1. 您现有的实现无法理解 int 值是否确实是连续的,因此您将需要添加一些代码来按顺序对从 resMap(kw).distinct 返回的 Int 进行排序为接下来的步骤做好准备。你可以弄清楚如何做到这一点。
  2. 然后您需要按连续性质对 Int 进行分组。例如,如果您有 (14,15,16,18,19,20,22),那么这确实需要进一步分组为 ((14,15,16),(18,19,20),(22))。你可以为此想出你的算法。
  3. 映射到外部集合(此时是Seq[Seq[Int]]),根据内部Seq的长度是否大于1有不同的处理。如果大于第一,您可以安全地调用 headtail 来获取渲染范围所需的 Int。或者,您可以更惯用地制作一个 for-comprehension,它由 headOptiontailOption 的值组成,以构建相同的范围字符串。您在问题中提到了长度为 3,因此您可以根据需要调整此步骤以满足该需求。
  4. 最后,现在您 Seq[String] 看起来像 ("14-16","18-20","22"),您需要使用类似于以下的 mkString 调用将它们连接在一起方括号中已有的内容

作为参考,您应该进一步了解 Seq 特性的 Scaladoc:

https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html

这是一种解决方法。

def collapseConsecutives(nums :Seq[Int]) :List[String] =
  nums.foldRight((nums.last, List.empty[List[Int]])) {
    case (n, (prev,acc)) if prev-n == 1 => (n, (n::acc.head) :: acc.tail)
    case (n, ( _  ,acc))                => (n, List(n) :: acc)
  }._2.map{ ns =>
    if (ns.length < 3) ns.mkString(",")  //1 or 2 non-collapsables
    else s"${ns.head}-${ns.last}"        //3 or more, collapsed
  }

用法:

println(kw + "\t\t" +
        collapseConsecutives(resMap(kw).distinct).mkString("[",",","]") + "\t\t" +
        resMap(kw).length)