scala之折叠案例类

bhlsheji 阅读:15 2024-11-24 20:56:43 评论:0

我遇到这样一种情况,我有几个案例类,其中所有变量都是可选的。

假设我有:

case class Size(width: Option[Int], height: Option[Int]) 
case class Foo(a: Option[String], b: Option[Boolean], c: Option[Char]) 

给定相同类型的案例类的集合,我想将它们折叠起来比较选项值并保留定义的值。 IE。对于大小:

values.foldLeft(x) { (a, b) => 
  Size(a.width.orElse(b.width), a.height.orElse(b.height)) 
} 

我想以更通用的方式对上述任何案例类执行此操作。我正在考虑用 unapply(_).get 等做点什么。有谁知道解决这个问题的聪明方法吗?

请您参考如下方法:

好的,考虑一下:

def foldCase[C,T1](unapply: C => Option[Option[T1]], apply: Option[T1] => C) 
  (coll: Seq[C]): C = { 
  coll.tail.foldLeft(coll.head) { case (current, next) => 
    apply(unapply(current).get orElse unapply(next).get) 
  } 
} 
 
case class Person(name: Option[String]) 
 
foldCase(Person.unapply, Person.apply)(List(Person(None), Person(Some("Joe")), Person(Some("Mary")))) 

可以重载 foldCase 以接受两个、三个或更多参数,每个参数一个版本的 f。然后它可以与任何案例类一起使用。由于需要担心元组的问题,下面是一种使其适用于案例类或两个参数的方法。将它扩展到更多参数是微不足道的,尽管有点烦人。

def foldCase[C,T1,T2](unapply: C => Option[(Option[T1], Option[T2])], apply: (Option[T1], Option[T2]) => C) 
  (coll: Seq[C]): C = { 
  def thisOrElse(current: (Option[T1], Option[T2]), next: (Option[T1], Option[T2])) = 
    apply(current._1 orElse next._1, current._2 orElse next._2) 
  coll.tail.foldLeft(coll.head) { case (current, next) => 
    thisOrElse(unapply(current).get, unapply(next).get) 
  } 
} 
 
val list = Person(None, None) :: Person(Some("Joe"), None) :: Person(None, Some(20)) :: Person(Some("Mary"), Some(25)) :: Nil 
 
def foldPerson = foldCase(Person.unapply, Person.apply) _ 
 
foldPerson(list) 

要重载使用它,只需将所有定义放在一个对象中:

object Folder { 
  def foldCase[C,T1](unapply: C => Option[Option[T1]], apply: Option[T1] => C) 
    (coll: Seq[C]): C = { 
    coll.tail.foldLeft(coll.head) { case (current, next) => 
      apply(unapply(current).get orElse unapply(next).get) 
    } 
  } 
 
  def foldCase[C,T1,T2](unapply: C => Option[(Option[T1], Option[T2])], apply: (Option[T1], Option[T2]) => C) 
    (coll: Seq[C]): C = { 
    def thisOrElse(current: (Option[T1], Option[T2]), next: (Option[T1], Option[T2])) = 
      apply(current._1 orElse next._1, current._2 orElse next._2) 
    coll.tail.foldLeft(coll.head) { case (current, next) => 
      thisOrElse(unapply(current).get, unapply(next).get) 
    } 
  } 
} 

但是,当您执行此操作时,您必须明确地将 applyunapply 转换为函数:

case class Question(answer: Option[Boolean]) 
val list2 = List(Question(None), Question(Some(true)), Question(Some(false))) 
Folder.foldCase(Question.unapply _, Question.apply _)(list2) 

也许可以把它变成一个结构类型,这样你只需要传递伴生对象,但我做不到。在 #scala 上,有人告诉我答案是否定的,至少我是如何处理这个问题的。


标签:Scala
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号