微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何改进这个“更新”功能?

如何解决如何改进这个“更新”功能?

假设我有 case class A(x: Int,s: String) 并且需要使用这样的 List[A] 更新 Map[Int,String]

def update(as: List[A],map: Map[Int,String]): List[A] = ???

val as = List(A(1,"a"),A(2,"b"),A(3,"c"),A(4,"d"))
val map = Map(2 -> "b1",4 -> "d1",5 -> "e",6 -> "f")
update(as,map) // List(A(1,"b1"),"d1"))

我是这样写update的:

def update(as: List[A],String]): List[A] = {

  @annotation.tailrec
  def loop(acc: List[A],rest: List[A],String]): List[A] = rest match {
    case Nil => acc
    case as => as.span(a => !map.contains(a.x)) match {
      case (xs,Nil) => xs ++ acc
      case (xs,y :: ys) => loop((y.copy(s = map(y.x)) +: xs) ++ acc,ys,map - y.x)
    }
  }

  loop(Nil,as,map).reverse
}

函数工作正常,但不是最佳的,因为当 map 为空时,它会继续迭代输入列表。此外,它看起来过于复杂。您建议如何改进此 update 函数

解决方法

如果你不能对ListMap做任何假设。那么最好的方法是迭代前者,以最简单的方式突出一次;也就是说,使用 map 函数。

list.map { a =>
  map
    .get(key = a.x)
    .fold(ifEmpty = a) { s =>
      a.copy(s = s)
   }
}

但是,当且仅当,您可以确定大部分时间:

  1. 列表会很大。
  2. 地图会很小。
  3. Map 中的键是 List 中值的子集。
  4. 并且所有操作都将更靠近列表的头部而不是尾部。

然后,您可以使用以下方法,这在这种情况下应该更有效。

def optimizedUpdate(data: List[A],updates: Map[Int,String]): List[A] = {
  @annotation.tailrec
  def loop(remaining: List[A],map: Map[Int,String],acc: List[A]): List[A] =
    if (map.isEmpty) acc reverse_::: remaining
    else remaining match {
      case a :: as =>
        map.get(key = a.x) match {
          case None =>
            loop(
              remaining = as,map,a :: acc
            )
          
          case Some(s) =>
            loop(
              remaining = as,map = map - a.x,a.copy(s = s) :: acc
            )
        }
      
      case Nil =>
        acc.reverse
    }
  
  loop(remaining = data,map = updates,acc = List.empty)
}

但是请注意,代码不仅更长,而且更难理解。
它实际上比map解决方案效率更低(如果条件不满足);这是因为 stdlib 实现 “欺骗” 并构造了 List 我改变了它的 tail 而不是向后构建它然后 reversing 它像我们一样有。

无论如何,与任何事物的性能一样,唯一真正的答案是基准测试。
但是,如果您确实需要速度,我会使用 map 解决方案只是为了清晰起见或使用可变方法。


您可以看到运行 here 的代码。

,

怎么样

def update(as: List[A],String]): List[A] =
  as.foldLeft(List.empty[A]) { (agg,elem) =>
    val newA = map
      .get(elem.x)
      .map(a => elem.copy(s = a))
      .getOrElse(elem)
    newA :: agg
  }.reverse

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。