如何替换Scala中列表中的第一个匹配项?

时间:2013-10-29 00:47:03

标签: scala immutability

我们说你有:

List(('a', 1), ('b', 1), ('c', 1), ('b', 1))

并且您希望将第一个('b', 1)替换为('b', 2),并且您不希望它(a)浪费时间评估第一场比赛并且(b)更新任何进一步的匹配元组。

在Scala中是否有相对简洁的方法(即,不将列表拆分并重新连接)。像假想函数mapFirst那样返回列表,第一个匹配值递增:

testList.mapFirst { case ('b', num) => ('b', num + 1) }

3 个答案:

答案 0 :(得分:2)

我猜你不必将整个列表分开。 (只有在找到元素之前)

def replaceFirst[A](a : List[A], repl : A, replwith : A) : List[A] = a match {
  case Nil => Nil
  case head :: tail => if(head == repl) replwith :: tail else head :: replaceFirst(tail, repl, replwith) 
}

呼叫例如:

replaceFirst(List(('a', 1), ('b', 1), ('c', 1), ('b', 1)), ('b', 1), ('b', 2))

结果:

List((a,1), (b,2), (c,1), (b,1))

具有部分功能和隐含的方式(看起来更像mapFirst):

implicit class MyRichList[A](val list: List[A]) {
    def mapFirst(func: PartialFunction[A, A]) = {
      def mapFirst2[A](a: List[A], func: PartialFunction[A, A]): List[A] = a match {
        case Nil => Nil
        case head :: tail => if (func.isDefinedAt(head)) func.apply(head) :: tail else head :: mapFirst2(tail, func)
      }
      mapFirst2(list, func)
    }
}

并像这样使用它:

List(('a', 1), ('b', 1), ('c', 1), ('b', 1)).mapFirst {case ('b', num) => ('b', num + 1)}

答案 1 :(得分:2)

您可以相对轻松地模拟此类功能。我能想到的最快(实现方式,不一定是性能方面)是这样的:

def replaceFirst[A](a:List[A], condition: (A)=>Boolean, transform:(A)=>(A)) = {
     val cutoff =a.indexWhere(condition)
     val (h,t) = a.splitAt(cutoff)
     h ++ (transform(t.head) :: t.tail)
}

scala> replaceFirst(List(1,2,3,4,5),{x:Int => x%2==0}, { x:Int=> x*2 })
res4: List[Int] = List(1, 4, 3, 4, 5)

scala> replaceFirst(List(('a',1),('b',2),('c',3),('b',4)), {m:(Char,Int) => m._1=='b'},{m:(Char,Int) => (m._1,m._2*2)})
res6: List[(Char, Int)] = List((a,1), (b,4), (c,3), (b,4))

答案 2 :(得分:1)

使用span仅查找第一个元素。即使case没有满足,它也不应该抛出异常。不用说,您可以根据需要指定任意数量的案例。

  implicit class MyRichieList[A](val l: List[A]) {

    def mapFirst(pf : PartialFunction[A, A]) = 
        l.span(!pf.isDefinedAt(_)) match {
           case (x, Nil) => x
           case (x, y :: ys) => (x :+ pf(y)) ++ ys
     }

  }

  val testList = List(('a', 1), ('b', 1), ('c', 1), ('b', 1))
  testList.mapFirst { 
      case ('b', n) => ('b', n + 1) 
      case ('a', 9) => ('z', 9) 
  }
  // result --> List((a,1), (b,2), (c,1), (b,1))