我有两个相同类型的实例化案例类.
case class Foo(x : Option[String], y : Option[String], z : Option[String])
Run Code Online (Sandbox Code Playgroud)
让我们调用实例化的类A和B.
val a = Foo(x=Some("foo"), y=Some("bar"), z=Some("baz"))
val b = Foo(x=None, y=Some("etch"), z=None)
Run Code Online (Sandbox Code Playgroud)
我想知道是否可以通过一般操作在单个操作中用B更新案例类A.
val c = b *oper* a // produces Foo(x=Some("foo"), y=Some("etch"), z=Some("baz"))
Run Code Online (Sandbox Code Playgroud)
参数设置为无忽略.理想情况下,操作也应该是通用的,因此它可以作用于任何类型的案例类.
我有一些直觉,可以通过先将类转换为元组/列表并在操作完成后转换回类来使用Scalaz来实现这一点 - 也许使用ApplicativeBuilder?我是否以正确的方式思考这个问题?有任何想法吗?
Tra*_*own 23
您可以使用半群实例来包含许多细节:
import scalaz._, Scalaz._
case class Foo(a: Option[String], b: Option[String], c: Option[String])
implicit object fooSemigroup extends Semigroup[Foo] {
def fromFoo(f: Foo) = (f.a.fst, f.b.fst, f.c.fst)
def toFoo(t: (FirstOption[String], FirstOption[String], FirstOption[String])) =
Foo(t._1.value, t._2.value, t._3.value)
def append(x: Foo, y: => Foo) = toFoo(fromFoo(x) |+| fromFoo(y))
}
Run Code Online (Sandbox Code Playgroud)
这给了我们:
scala> val a = Foo(Some("foo"), Some("bar"), Some("baz"))
a: Foo = Foo(Some(foo),Some(bar),Some(baz))
scala> val b = Foo(None, Some("etch"), None)
b: Foo = Foo(None,Some(etch),None)
scala> b |+| a
res11: Foo = Foo(Some(foo),Some(etch),Some(baz))
Run Code Online (Sandbox Code Playgroud)
我认为这是你想要的,虽然它不是很一般.
如果你想要一些适用于所有case类的东西(给定成员的相应类型类实例),你可以使用Shapeless和Scalaz 的以下组合.请注意,我正在利用missfactor的答案以及Miles Sabin的这个例子.首先是一些monoid实例:
import scalaz._, Scalaz._
import shapeless._, HList._
implicit object hnilMonoid extends Monoid[HNil] {
val zero = HNil
def append(a: HNil, b: => HNil) = HNil
}
implicit def hlistMonoid[H, T <: HList](
implicit mh: Monoid[H],
mt: Monoid[T]
): Monoid[H :: T] = new Monoid[H :: T] {
val zero = mh.zero :: mt.zero
def append(a: H :: T, b: => H :: T) =
(a.head |+| b.head) :: (a.tail |+| b.tail)
}
implicit def caseClassMonoid[C, L <: HList](
implicit iso: Iso[C, L],
ml: Monoid[L]
) = new Monoid[C] {
val zero = iso.from(ml.zero)
def append(a: C, b: => C) = iso.from(iso.to(a) |+| iso.to(b))
}
Run Code Online (Sandbox Code Playgroud)
接下来为了简单起见我只是将"First"monoid实例Option放在范围内,而不是FirstOption像上面那样使用包装器.
implicit def optionFirstMonoid[A] = new Monoid[Option[A]] {
val zero = None
def append(a: Option[A], b: => Option[A]) = a orElse b
}
Run Code Online (Sandbox Code Playgroud)
现在我们的案例类:
case class Foo(a: Option[String], b: Option[String], c: Option[String])
Run Code Online (Sandbox Code Playgroud)
并将Iso实例转换为a HList和back:
implicit def fooIso = Iso.hlist(Foo.apply _, Foo.unapply _)
Run Code Online (Sandbox Code Playgroud)
我们完成了:
scala> val a = Foo(Some("foo"), Some("bar"), Some("baz"))
a: Foo = Foo(Some(foo),Some(bar),Some(baz))
scala> val b = Foo(None, Some("etch"), None)
b: Foo = Foo(None,Some(etch),None)
scala> b |+| a
res0: Foo = Foo(Some(foo),Some(etch),Some(baz))
Run Code Online (Sandbox Code Playgroud)
你可以使用半群,而不是幺这里也和保存几行,但我试图从尽可能多的复制和粘贴脱身shapeless/examples代码越好,所以我会离开,作为一个练习.
为了解决您对性能的评论,对于后一种解决方案与使用orElse(Scala 2.9.2,IcedTea7 2.2.1)的标准库解决方案相比,这是一个完全不科学的基准:
def add(x: Foo, y: Foo) = Foo(x.a orElse y.a, x.b orElse y.b, x.c orElse y.c)
def ros = if (util.Random.nextBoolean)
Some(util.Random.nextString(util.Random.nextInt(10))) else None
val foos = Seq.fill(500000)(Foo(ros, ros, ros))
def time(block: => Unit) = {
val start = System.currentTimeMillis
(block, System.currentTimeMillis - start)
}
Run Code Online (Sandbox Code Playgroud)
然后运行几次后:
scala> Iterator.fill(10)(time(foos.reduce(add(_, _)))._2).sum / 10
res4: Long = 49
scala> Iterator.fill(10)(time(foos.reduce(_ |+| _))._2).sum / 10
res5: Long = 265
Run Code Online (Sandbox Code Playgroud)
有点令人惊讶的是,Shapeless-less Scalaz解决方案有点慢:
scala> Iterator.fill(10)(time(foos.reduce(_.|+|(_)(fooSemigroup)))._2).sum / 10
res6: Long = 311
Run Code Online (Sandbox Code Playgroud)
但正如我所说,这是一种非常缺乏基准测试的方法,你应该运行自己的(Caliper是一个很棒的库).
无论如何,是的,你要为抽象付出代价,但不是那么多,而且它往往是值得的.
mis*_*tor 13
正如本主题所讨论的那样,这就是如何以完全类型安全的方式解决无形状的问题.
scala> import shapeless._
import shapeless._
scala> import HList._
import HList._
scala> case class Foo(a: Option[Int], b: Option[Int])
defined class Foo
scala> val a = Foo(Some(3), None)
a: Foo = Foo(Some(3),None)
scala> val b = Foo(Some(22), Some(1))
b: Foo = Foo(Some(22),Some(1))
scala> implicit val fooIso = HListIso(Foo.apply _, Foo.unapply _)
fooIso: shapeless.HListIso[Foo,shapeless.::[Option[Int],shapeless.::[Option[Int],shapeless.HNil]]] = shapeless.HListIso@11c5b77
scala> type O2[+A] = (Option[A], Option[A])
defined type alias O2
scala> object mapper extends (O2 ~> Option) {
| def apply[A](x: O2[A]): Option[A] = x._1.orElse(x._2)
| }
defined module mapper
scala> fooIso.fromHList(fooIso.toHList(a).zip(fooIso.toHList(b)).map(mapper))
res13: Foo = Foo(Some(3),Some(1))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2135 次 |
| 最近记录: |