gpt4 book ai didi

scala - 用于更好地链接收集的功能模式

转载 作者:行者123 更新时间:2023-12-03 15:18:44 24 4
gpt4 key购买 nike

我经常发现自己需要链接collects我想在一次遍历中进行多次收集。对于与任何集合都不匹配的东西,我还想返回一个“剩余部分”。
例如:

sealed trait Animal
case class Cat(name: String) extends Animal
case class Dog(name: String, age: Int) extends Animal

val animals: List[Animal] =
List(Cat("Bob"), Dog("Spot", 3), Cat("Sally"), Dog("Jim", 11))

// Normal way
val cats: List[Cat] = animals.collect { case c: Cat => c }
val dogAges: List[Int] = animals.collect { case Dog(_, age) => age }
val rem: List[Animal] = Nil // No easy way to create this without repeated code
这真的不是很好,它需要多次迭代并且没有合理的方法来计算余数。我可以写一个非常复杂的折叠来解决这个问题,但这真的很讨厌。
相反,我通常选择与您在 fold 中的逻辑非常相似的突变。 :
import scala.collection.mutable.ListBuffer

// Ugly, hide the mutation away
val (cats2, dogsAges2, rem2) = {
// Lose some benefits of type inference
val cs = ListBuffer[Cat]()
val da = ListBuffer[Int]()
val rem = ListBuffer[Animal]()
// Bad separation of concerns, I have to merge all of my functions
animals.foreach {
case c: Cat => cs += c
case Dog(_, age) => da += age
case other => rem += other
}
(cs.toList, da.toList, rem.toList)
}
我不喜欢这一点,它具有更糟糕的类型推断和关注点分离,因为我必须合并所有不同的部分函数。它还需要很多行代码。
我想要的是一些有用的模式,比如 collect返回余数(我承认 partitionMap 2.13 中的新功能可以做到这一点,但更丑陋)。我也可以使用某种形式的 pipemap用于对元组的部分进行操作。以下是一些组成的实用程序:
implicit class ListSyntax[A](xs: List[A]) {
import scala.collection.mutable.ListBuffer
// Collect and return remainder
// A specialized form of new 2.13 partitionMap
def collectR[B](pf: PartialFunction[A, B]): (List[B], List[A]) = {
val rem = new ListBuffer[A]()
val res = new ListBuffer[B]()
val f = pf.lift
for (elt <- xs) {
f(elt) match {
case Some(r) => res += r
case None => rem += elt
}
}
(res.toList, rem.toList)
}
}
implicit class Tuple2Syntax[A, B](x: Tuple2[A, B]){
def chainR[C](f: B => C): Tuple2[A, C] = x.copy(_2 = f(x._2))
}
现在,我可以用一种可以在单次遍历中完成的方式(使用惰性数据结构)来编写它,并且遵循功能性的、不可变的实践:
// Relatively pretty, can imagine lazy forms using a single iteration
val (cats3, (dogAges3, rem3)) =
animals.collectR { case c: Cat => c }
.chainR(_.collectR { case Dog(_, age) => age })
我的问题是,有这样的模式吗?它闻起来有点像 Cats、FS2 或 ZIO 这样的库中的东西,但我不确定它会叫什么。
代码示例的 Scastie 链接: https://scastie.scala-lang.org/Egz78fnGR6KyqlUTNTv9DQ

最佳答案

我想看看 fold() 有多“讨厌”将是。

val (cats
,dogAges
,rem) = animals.foldRight((List.empty[Cat]
,List.empty[Int]
,List.empty[Animal])) {
case (c:Cat, (cs,ds,rs)) => (c::cs, ds, rs)
case (Dog(_,d),(cs,ds,rs)) => (cs, d::ds, rs)
case (r, (cs,ds,rs)) => (cs, ds, r::rs)
}
我想是旁观者的眼睛。

关于scala - 用于更好地链接收集的功能模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63275255/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com