2

假设您有:

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) }

4

3 回答 3

3

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

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)}
于 2013-10-29T01:04:45.780 回答
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))
于 2013-10-29T01:25:54.033 回答
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))
于 2013-10-29T09:18:49.433 回答