【问题标题】:Scala iterate over listScala迭代列表
【发布时间】:2017-01-19 21:00:27
【问题描述】:

如何在不使用可变集合的情况下将List(1,2,30,13,4) 转换为List(1,2,3,4)

30是一种转义数;如果找到,则应将其删除,并且下一个数字应减少10。

【问题讨论】:

  • 所以你需要一种方法来逃避 30 并将 13 减少 10?如果数据输入更改为其他值怎么办?

标签: list scala collections functional-programming


【解决方案1】:

按照@Victor 的评论和概括:

def escapeAdjust[A](xs: List[A], p: A => Boolean, f: A => A): List[A] = {
  @tailrec
  def loop(ys: List[A], p: A => Boolean, f: A => A, acc: List[A]): List[A] = ys match {
    case Nil => acc
    case h :: Nil => if (p(h)) acc else h :: acc
    case h :: next :: rest => if (p(h)) loop(f(next) :: rest, p, f, acc)
                              else loop(next :: rest, p, f, h :: acc)
  }
  loop(xs, p, f, List[A]()).reverse
}

用法:

val p = (x: Int) => x == 30
val f = (y: Int) => y - 10

escapeAdjust(List[Int](1,2,30,13,4), p, f)
res12: List[Int] = List(1, 2, 3, 4)

escapeAdjust(List(1,2,30,13,4,30,15,6), p, f)
res13: List[Int] = List(1, 2, 3, 4, 5, 6)

escapeAdjust(List(1,3,30,40), p, f)
res14: List[Int] = List(1, 3)

escapeAdjust(List(30,11,2), p, f)
res15: List[Int] = List(1, 2)

escapeAdjust(List(1,3,30,40, 5), p, f)
res16: List[Int] = List(1, 3, -5)

【讨论】:

    【解决方案2】:

    将前一个项目压缩到每个项目,然后使用collect 和一个偏函数决定是否返回以及返回什么:

    l.zip(l.head :: l).collect {
      case (v, 30) if v != 30 => v - 10  // only previous is 30 - decrease 10
      case (v, prev) if v != 30 => v     // none of them is 30 - just return value
      // skipping (filtering out) case where v == 30
    }
    

    例如:

    • 对于val l = List(1,2,30,13,4,30,15,6),这将返回List(1,2,3,4,5,6)
    • 对于val l = List(1,3,30,40),返回List(1,3,30)
    • 对于val l = List(30,11,2),返回List(1,2)

    【讨论】:

    • 考虑一个以30开头的列表。
    【解决方案3】:

    您可能应该检查您的代码/用例,因为这有点难闻,无论如何:

    List(1,2,30,13,4).foldLeft((List.empty[Int], false)) {
      case ((accumulator, wasFound), next) =>
        if(wasFound) (accumulator :+ (next - 10), false)
        else if(next == 30) (accumulator, true)
        else (accumulator :+ next, false)
    }._1
    

    基本上,您可以保留一个布尔值和一个累加器,并使用它来记住是否找到了 30,并在找到时采取适当的措施。

    请注意,如果您有 List(1,3,30,40) 之类的内容,这对您没有帮助,输出将是 List(1,3,30),您应该明确这种情况是否可以接受,否则我将使用递归解决方案允许迭代一个元素两次,以防它是 30:

    scala> def loop(list: List[Int], acc: List[Int], wasFound: Boolean, toRemove: Int): List[Int] = list match {
     |     case h :: t =>
     |       if(wasFound) loop((h - 10) :: t, acc, false, toRemove)
     |       else if(h == toRemove) loop(t, acc, true, toRemove)
     |       else loop(t, acc :+ h, false, toRemove)
     |     case Nil =>
     |       acc
     |   }
    loop: (list: List[Int], acc: List[Int], wasFound: Boolean, toRemove: Int)List[Int]
    
    scala> loop(List(1,2,30,13,4), List(), false, 30)
    res1: List[Int] = List(1, 2, 3, 4)
    
    scala> loop(List(1,2,30,40, 13,4), List(), false, 30)
    res2: List[Int] = List(1, 2, 3, 4)
    

    逻辑非常相似,唯一的区别是您将在 30 之后找到的那个迭代两次,这样如果是另一个 30,它就会被删除,下一个会减少。

    【讨论】:

    • 使用 3 个case 子句可能看起来更简洁,例如case ((accumulator, true), next) => ...; case ((accumulator, _), 30) => ...。另外,不要附加到List,无论是前置还是反转或使用ListBuffer
    猜你喜欢
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    • 2011-07-15
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    相关资源
    最近更新 更多