【问题标题】:How to remove duplicate ints before a certain element in Scala如何在Scala中的某个元素之前删除重复的整数
【发布时间】:2021-01-26 07:59:38
【问题描述】:

我有一个列表,可以在我不需要的索引之前包含某些数字。例如:

val tempList: List[Any] = List(0,0,0,0,0,3,.,5,0,2,5)

如何在不过滤掉 3 之后的 0 的情况下删除 3 之前的所有 0?

【问题讨论】:

  • Any 是一种代码异味,最好重新设计您的问题以避免它。
  • List(0,1,0,3,0) 的结果应该是什么?是List(1,0,3,0) 还是List(1,3,0) 还是别的什么?

标签: list scala filter functional-programming scala-collections


【解决方案1】:

您可以使用dropWhile。假设您要删除 1 之前的所有内容:

scala> val l = List(0, 0, 0, 0, 1, 2, 0)
l: List[Int] = List(0, 0, 0, 0, 1, 2, 0)

scala> l.dropWhile(_ == 0)
res1: List[Int] = List(1, 2, 0)

【讨论】:

  • 我认为这应该返回 List(1,2) 而不是 List(1,2,0) 因为该任务是“删除 3 之前的所有 0”并且因为您的示例数据没有 3 这意味着所有 0 .
【解决方案2】:

您需要做的是对dropWhile 进行修改,我将其称为filterWhile

利用 Collection API 的一个简单解决方案如下:

def filterWhile[A](
    list: List[A]
)(filterP: A => Boolean, whileP: A => Boolean): List[A] = {
  val (toFilter, unfiltered) = list.span(whileP)
  toFilter.filter(filterP) ++ unfiltered
}

您可以使用此代码 here on Scastie,并进行一些测试以验证其是否按预期工作,如下所示:

def test[A](
    input: List[A],
    expected: List[A]
)(filterP: A => Boolean, whileP: A => Boolean): Unit =
  assert(
    filterWhile(input)(filterP, whileP) == expected,
    s"input: $input, expected: $expected, got: ${filterWhile(input)(filterP, whileP)}"
  )

test(
  input = List(0, 0, 0, 0, 0, 3, '.', 5, 0, 2, 5),
  expected = List(3, '.', 5, 0, 2, 5)
)(filterP = _ != 0, whileP = _ != 3)

test(
  input = List(0, 0, 0, 1, 0, 3, '.', 5, 0, 2, 5),
  expected = List(1, 3, '.', 5, 0, 2, 5)
)(filterP = _ != 0, whileP = _ != 3)

test(
  input = List(1, 2, 3, 4),
  expected = List(1, 2, 3, 4)
)(filterP = _ < 5, whileP = _ < 5)

在您的情况下,您所要做的就是按如下方式调用该函数:

filterWhile(tempList)(_ != 0, _ != 3)

第一个谓词说明如何过滤,第二个谓词定义“while”子句。我选择将谓词顺序与函数名称对齐(它首先说“过滤器”,然后是“while”),但可以根据您的喜好随意调整。无论如何,在这里使用命名参数可能是一件好事。

【讨论】:

    【解决方案3】:

    结合列表上的现有功能,我想出了:

    def filterUntil[A](list: List[A], filter: A => Boolean, cond: A => Boolean): List[A] = {
        list.takeWhile(cond).filter(filter) ++ list.dropWhile(cond)
    }
    

    这产生的结果与@stefanobaghino 的答案大致相同。

    【讨论】:

      【解决方案4】:

      这可以通过foldLeft 来完成,但在这种情况下,我会得到一个递归例程:

      def remove(list: List[Any]): List[Any] = {
        def loop(rem: List[Any], res: List[Any]): List[Any] =
          rem match {
            case Nil =>
              res
            case 0 :: tail =>
              loop(tail, res)
            case 3 :: _ =>
              res ++ rem
            case x :: tail =>
              loop(tail, res :+ x)
            }
      
        loop(list, Nil)
      }
      
      remove(List(0,0,0,0,0,3,".",5,0,2,5))
      // List(3, ., 5, 0, 2, 5)
      

      请注意,这对于长列表效率不高,因为添加到 List 的尾部会非常慢。

      此外,如 cmets 中所述,您应尽可能避免使用 Any 并使用更具体的类型。

      [感谢@MikhailIonkin 的简化]

      【讨论】:

      • 太复杂了。 Scala 是为编写简单的代码而创建的。这个例子反转​​它。此代码还使用 Any 作为参数类型。通常这是一种不好的做法
      • @MikhailIonkin 如果您有更简单的解决方案,请发布。答案使用Any,因为问题使用Any,我明确地说这是一个坏主意。
      • 例如,您可以将(x: Int) :: tail if x == 0 简化为0 :: tail。另外我认为最好重命名remove 函数并重命名loop 的参数。
      猜你喜欢
      • 2013-03-27
      • 2020-05-10
      • 2019-11-13
      • 2021-07-02
      • 1970-01-01
      • 2017-04-14
      • 2014-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多