首页 > 解决方案 > 列出其元素依赖于之前的元素

问题描述

假设我有一个递增整数的列表。如果 2 个连续数字的差值小于阈值,则我们按相同的数字对它们进行索引,从 0 开始。否则,我们将索引增加 1。

例如:对于列表 (1,2,5,7,8,11,15,16,20) 并且阈值 = 3,输出将是:(0, 0, 1, 1, 1, 2, 3 , 3, 4)。

这是我的代码:

  val listToGroup = List(1,2,5,7,8,11,15,16,20)
  val diff_list = listToGroup.sliding(2,1).map{case List(i, j) => j-i}.toList

  val thres = 2

  var j=0
  val output_ = for(i <- diff_list.indices) yield {
    if (diff_list(i) > thres ) {
      j += 1
    }
    j
  }
  val output = List.concat(List(0), output_)

我是 Scala 的新手,我觉得这个列表没有得到有效使用。如何改进此代码?

标签: scala

解决方案


scanLeft您可以通过使用获得更惯用的代码来避免可变变量:

val output = diff_list.scanLeft(0) { (count, i) => 
  if (i > thres) count + 1
  else count
}

您的代码显示了一些在 Scala 中通常会避免的结构,但在来自过程语言时很常见,例如:for(i <- diff_list.indices) ... diff_list(i)可以替换为for(i <- diff_list).

除此之外,我认为您的代码是高效的 - 无论如何您都需要遍历列表并在O(N). 我不会担心这里的效率,更多的是风格和可读性。

我对我认为在 Scala 中对于整个代码来说更自然的方式的重写是:

val listToGroup = List(1,2,5,7,8,11,15,16,20)

val thres = 2

val output = listToGroup.zip(listToGroup.drop(1)).scanLeft(0) { case (count, (i, j)) => 
  if (j - i > thres) count + 1
  else count
}

我对您的代码的调整:

  • scanLeft用来执行结果集合构造
  • 我更喜欢x.zip(x.drop(1))x.sliding(2, 1)构造元组似乎比构造集合更有效)。您也可以使用x.zip(x.tail),但这不能处理空x
  • 我避免临时结果diff_list

推荐阅读