如何在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 并不是一个众包家庭作业或项目答案的地方。我会让你相信事实并非如此。
也就是说,我希望你能从这个建议中获得一些关于分解这个问题的理解:
- 您现有的实现无法理解 int 值是否确实是连续的,因此您将需要添加一些代码来按顺序对从
resMap(kw).distinct
返回的 Int
进行排序为接下来的步骤做好准备。你可以弄清楚如何做到这一点。
- 然后您需要按连续性质对
Int
进行分组。例如,如果您有 (14,15,16,18,19,20,22)
,那么这确实需要进一步分组为 ((14,15,16),(18,19,20),(22))
。你可以为此想出你的算法。
- 映射到外部集合(此时是
Seq[Seq[Int]]
),根据内部Seq
的长度是否大于1有不同的处理。如果大于第一,您可以安全地调用 head
和 tail
来获取渲染范围所需的 Int
。或者,您可以更惯用地制作一个 for-comprehension,它由 headOption
和 tailOption
的值组成,以构建相同的范围字符串。您在问题中提到了长度为 3,因此您可以根据需要调整此步骤以满足该需求。
- 最后,现在您
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)
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 并不是一个众包家庭作业或项目答案的地方。我会让你相信事实并非如此。
也就是说,我希望你能从这个建议中获得一些关于分解这个问题的理解:
- 您现有的实现无法理解 int 值是否确实是连续的,因此您将需要添加一些代码来按顺序对从
resMap(kw).distinct
返回的Int
进行排序为接下来的步骤做好准备。你可以弄清楚如何做到这一点。 - 然后您需要按连续性质对
Int
进行分组。例如,如果您有(14,15,16,18,19,20,22)
,那么这确实需要进一步分组为((14,15,16),(18,19,20),(22))
。你可以为此想出你的算法。 - 映射到外部集合(此时是
Seq[Seq[Int]]
),根据内部Seq
的长度是否大于1有不同的处理。如果大于第一,您可以安全地调用head
和tail
来获取渲染范围所需的Int
。或者,您可以更惯用地制作一个 for-comprehension,它由headOption
和tailOption
的值组成,以构建相同的范围字符串。您在问题中提到了长度为 3,因此您可以根据需要调整此步骤以满足该需求。 - 最后,现在您
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)