如何对列表元素的类型进行模式匹配
How to pattern match on the types of list elements
我想根据对象的类型对对象列表进行模式匹配。
但是将模式指定为 case x: List[ObjectType]
似乎不起作用。
以这个程序为例
sealed trait A
case class B() extends A
case class C() extends A
def func(theList: List[A]) = theList match
{
case listOfB: List[B] => println("All B's")
case listOfC: List[C] => println("All C's")
case _ => println("Somthing else")
}
func(List(C(), C(), C())) // prints: "All B's"
虽然列表只包含C,case模式指定了B的列表,但是match语句将其识别为B的列表?
我知道我可以像这样检查列表的每个元素:
case listOfA: List[A] if listOfA.forall{case B() => true case _ => false} => println("All B's")
但是比较麻烦,我在尝试使用的时候必须说明它确实是一个B的列表(listOfA.asInstanceOf[List[B]]
)
我怎样才能以更聪明/更好的方式做到这一点?
尝试自定义提取器以减少模式匹配的麻烦
object AllB {
def unapply(listOfA: List[A]): Boolean =
listOfA.forall { case B() => true; case _ => false }
}
object AllC {
def unapply(listOfA: List[A]): Boolean =
listOfA.forall { case C() => true; case _ => false }
}
def func(theList: List[A]) = theList match {
case AllB() => println("All B's")
case AllC() => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
或
import cats.implicits._
object AllB {
def unapply(listOfA: List[A]): Option[List[B]] =
listOfA.traverse { case b@B() => Some(b); case _ => None }
}
object AllC {
def unapply(listOfA: List[A]): Option[List[C]] =
listOfA.traverse { case c@C() => Some(c); case _ => None }
}
def func(theList: List[A]) = theList match {
case AllB(listOfB) => println("All B's")
case AllC(listOfC) => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
或者您可以定义一个 class 来创建所有必要的提取器并删除代码重复
class All[SubT: ClassTag] {
def unapply[T >: SubT](listOfA: List[T]): Option[List[SubT]] =
listOfA.traverse { case x: SubT => Some(x); case _ => None }
}
object AllB extends All[B]
object AllC extends All[C]
// val AllB = new All[B]
// val AllC = new All[C]
def func(theList: List[A]) = theList match {
case AllB(listOfB) => println("All B's")
case AllC(listOfC) => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
我想,最简单的就是使用 Shapeless
import shapeless.TypeCase
val AllB = TypeCase[List[B]]
val AllC = TypeCase[List[C]]
def func(theList: List[A]) = theList match {
case AllB(listOfB) => println("All B's")
case AllC(listOfC) => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#type-safe-cast
在 Shapeless 类型中定义了 class Typeable
。只是它的列表实例定义比 @LuisMiguelMejíaSuárez 的答案(即使用运行时反射)
/** Typeable instance for `Traversable`.
* Note that the contents be will tested for conformance to the element type. */
implicit def genTraversableTypeable[CC[X] <: Iterable[X], T]
(implicit mCC: ClassTag[CC[_]], castT: Typeable[T]): Typeable[CC[T] with Iterable[T]] =
// Nb. the apparently redundant `with Iterable[T]` is a workaround for a
// Scala 2.10.x bug which causes conflicts between this instance and `anyTypeable`.
new Typeable[CC[T]] {
def cast(t: Any): Option[CC[T]] =
if(t == null) None
else if(mCC.runtimeClass isInstance t) {
val cc = t.asInstanceOf[CC[Any]]
if(cc.forall(_.cast[T].isDefined)) Some(t.asInstanceOf[CC[T]])
else None
} else None
def describe = s"${safeSimpleName(mCC)}[${castT.describe}]"
}
另请参阅在 Scala 中模式匹配泛型类型的方法 https://gist.github.com/jkpl/5279ee05cca8cc1ec452fc26ace5b68b
假设您在编译时有一个 List[B]
或一个 List[C]
并且您想以不同的方式操作它们,您可以使用 typeclass.
像这样:
trait MyTypeClass[T] {
def process(data: List[T]): String
}
sealed trait A extends Product with Serializable
final case class B() extends A
final case class C() extends A
object A extends ALowerPriority {
implicit final val AllOfB: MyTypeClass[B] =
(_: List[B]) => "All B's"
implicit final val AllOfC: MyTypeClass[C] =
(_: List[C]) => "All C's"
}
trait ALowerPriority {
implicit final val Mixed: MyTypeClass[A] =
(_: List[A]) => "Somenthing else"
}
def func[T](theList: List[T])
(implicit ev: MyTypeClass[T]): Unit =
println(ev.process(data = theList))
它是这样工作的:
val bs = List(B(), B(), B())
val cs = List(C(), C(), C())
val mixed = List(C(), B(), C())
func(bs) // All B's
func(cs) // All C's
func(mixed) // Something else
注意:您需要考虑在您的类型类上公开的接口是什么,这样您就可以编写通用函数,但它们的行为会根据底层类型而有所不同。
但是,请记住 类型类 是在编译时选择的并且只使用类型。所以,如果你有一个 List[A]
类型的编译时值,即使它充满了 Bs
它也会选择 "Something else"
:
val as: List[A] = List(B(), B(), B())
func(as) // Something else
可以看到代码运行 here.
我想根据对象的类型对对象列表进行模式匹配。
但是将模式指定为 case x: List[ObjectType]
似乎不起作用。
以这个程序为例
sealed trait A
case class B() extends A
case class C() extends A
def func(theList: List[A]) = theList match
{
case listOfB: List[B] => println("All B's")
case listOfC: List[C] => println("All C's")
case _ => println("Somthing else")
}
func(List(C(), C(), C())) // prints: "All B's"
虽然列表只包含C,case模式指定了B的列表,但是match语句将其识别为B的列表?
我知道我可以像这样检查列表的每个元素:
case listOfA: List[A] if listOfA.forall{case B() => true case _ => false} => println("All B's")
但是比较麻烦,我在尝试使用的时候必须说明它确实是一个B的列表(listOfA.asInstanceOf[List[B]]
)
我怎样才能以更聪明/更好的方式做到这一点?
尝试自定义提取器以减少模式匹配的麻烦
object AllB {
def unapply(listOfA: List[A]): Boolean =
listOfA.forall { case B() => true; case _ => false }
}
object AllC {
def unapply(listOfA: List[A]): Boolean =
listOfA.forall { case C() => true; case _ => false }
}
def func(theList: List[A]) = theList match {
case AllB() => println("All B's")
case AllC() => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
或
import cats.implicits._
object AllB {
def unapply(listOfA: List[A]): Option[List[B]] =
listOfA.traverse { case b@B() => Some(b); case _ => None }
}
object AllC {
def unapply(listOfA: List[A]): Option[List[C]] =
listOfA.traverse { case c@C() => Some(c); case _ => None }
}
def func(theList: List[A]) = theList match {
case AllB(listOfB) => println("All B's")
case AllC(listOfC) => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
或者您可以定义一个 class 来创建所有必要的提取器并删除代码重复
class All[SubT: ClassTag] {
def unapply[T >: SubT](listOfA: List[T]): Option[List[SubT]] =
listOfA.traverse { case x: SubT => Some(x); case _ => None }
}
object AllB extends All[B]
object AllC extends All[C]
// val AllB = new All[B]
// val AllC = new All[C]
def func(theList: List[A]) = theList match {
case AllB(listOfB) => println("All B's")
case AllC(listOfC) => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
我想,最简单的就是使用 Shapeless
import shapeless.TypeCase
val AllB = TypeCase[List[B]]
val AllC = TypeCase[List[C]]
def func(theList: List[A]) = theList match {
case AllB(listOfB) => println("All B's")
case AllC(listOfC) => println("All C's")
case _ => println("Something else")
}
func(List(B(), B(), B())) // All B's
func(List[A](B(), B(), B())) // All B's
func(List(C(), C(), C())) // All C's
func(List(C(), B(), C())) // Something else
https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#type-safe-cast
在 Shapeless 类型中定义了 class Typeable
。只是它的列表实例定义比 @LuisMiguelMejíaSuárez 的答案(即使用运行时反射)
/** Typeable instance for `Traversable`. * Note that the contents be will tested for conformance to the element type. */ implicit def genTraversableTypeable[CC[X] <: Iterable[X], T] (implicit mCC: ClassTag[CC[_]], castT: Typeable[T]): Typeable[CC[T] with Iterable[T]] = // Nb. the apparently redundant `with Iterable[T]` is a workaround for a // Scala 2.10.x bug which causes conflicts between this instance and `anyTypeable`. new Typeable[CC[T]] { def cast(t: Any): Option[CC[T]] = if(t == null) None else if(mCC.runtimeClass isInstance t) { val cc = t.asInstanceOf[CC[Any]] if(cc.forall(_.cast[T].isDefined)) Some(t.asInstanceOf[CC[T]]) else None } else None def describe = s"${safeSimpleName(mCC)}[${castT.describe}]" }
另请参阅在 Scala 中模式匹配泛型类型的方法 https://gist.github.com/jkpl/5279ee05cca8cc1ec452fc26ace5b68b
假设您在编译时有一个 List[B]
或一个 List[C]
并且您想以不同的方式操作它们,您可以使用 typeclass.
像这样:
trait MyTypeClass[T] {
def process(data: List[T]): String
}
sealed trait A extends Product with Serializable
final case class B() extends A
final case class C() extends A
object A extends ALowerPriority {
implicit final val AllOfB: MyTypeClass[B] =
(_: List[B]) => "All B's"
implicit final val AllOfC: MyTypeClass[C] =
(_: List[C]) => "All C's"
}
trait ALowerPriority {
implicit final val Mixed: MyTypeClass[A] =
(_: List[A]) => "Somenthing else"
}
def func[T](theList: List[T])
(implicit ev: MyTypeClass[T]): Unit =
println(ev.process(data = theList))
它是这样工作的:
val bs = List(B(), B(), B())
val cs = List(C(), C(), C())
val mixed = List(C(), B(), C())
func(bs) // All B's
func(cs) // All C's
func(mixed) // Something else
注意:您需要考虑在您的类型类上公开的接口是什么,这样您就可以编写通用函数,但它们的行为会根据底层类型而有所不同。
但是,请记住 类型类 是在编译时选择的并且只使用类型。所以,如果你有一个 List[A]
类型的编译时值,即使它充满了 Bs
它也会选择 "Something else"
:
val as: List[A] = List(B(), B(), B())
func(as) // Something else
可以看到代码运行 here.