将 Scala Iterable[T] 转换为 Option[Iterable[S]],如果所有 T 都是 S 的实例,则填充?
Posted
技术标签:
【中文标题】将 Scala Iterable[T] 转换为 Option[Iterable[S]],如果所有 T 都是 S 的实例,则填充?【英文标题】:Converting a Scala Iterable[T] to an Option[Iterable[S]], populated if ALL the T are instances of S? 【发布时间】:2021-09-09 01:10:53 【问题描述】:我有一种模糊的感觉,这种方法可能存在(可能在Cats?) - 目的是转为Iterable[T]
(或Set[T]
是我真正感兴趣的) 转换为Option[Iterable[S]]
- 结果是:
Some[Iterable[S]]
:如果Iterable[T]
中的所有 条目是S
的实例
None
:如果Iterable[T]
中的任何条目不是S
的实例
我知道在 Cats 中已经有一种类似的方法,由 Alternative
授予 - 它是 separate
:
import cats.implicits._
import alleycats.std.set._
val stringsAndInts: Set[Either[String, Int]] = Set(Right(6),Left("Foo"))
val (strings: Set[String], ints: Set[Int]) = stringsAndInts.separate
...这对Set[Either[A,B]
很有效——但对于这个问题,我只对任何旧的Set[T]
感兴趣。
collect
呢?
此代码使用 Scala 标准库方法 collect
将编译 - 但请注意,它始终会生成 Set[S]
- 即使某些 setOfT
不是 S
的实例:
val setOfT: Set[T] = ???
val setOfS: Set[S] = setOfT.collect case s: S => s
【问题讨论】:
如果你有任意类型,由于类型擦除,这不能开箱即用。您要么需要为某些类型的子集编写特定的方法,要么创建自己的 typeclass 来验证数据。 - 或者您可以使用运行时反射并为类型擦除打开错误之门。 【参考方案1】:考虑无形类型安全铸造
import shapeless._
import syntax.typeable._
val xs: Set[Any] = Set("picard", "worf")
xs.cast[Set[Int]]
// res1: Option[Set[Int]] = None
xs.cast[Set[String]]
// res2: Option[Set[String]] = Some(value = Set("picard", "worf"))
如果您希望拥有与 collect
类似的功能并且在类路径中已经有了猫,那么这里是单线
import alleycats.std.set._
animals.map(implicitly[ClassTag[Dog]].unapply).sequence
Option[Set[Dog]]
的类型。
【讨论】:
你好马里奥!很棒的东西——所以这是使用 Scala 宏来绕过类型擦除,对吗? github.com/milessabin/shapeless/blob/main/core/src/main/scala/… @RobertoTyley 是的,与ClassTag#unapply 不同,它也适用于参数化类型。 docs【参考方案2】:正如 Luis Miguel Mejía Suárez 在评论中指出的那样,由于类型擦除,我想要的方法无法用于任意类型。
最后,我写了这样的代码:
val animals: Set[Animal] = ???
val optDogs: Option[Set[Dog]] = animals.foldLeft(Option(Set.empty[Dog]))
case (oDogs, dog: Dog) => oDogs.map(_ + dog)
case _ => None
...很高兴看到更好的实现!
【讨论】:
你可以只做aminals.collect case dog: Dog => dog
并检查它是否为非空。你也可以做animals.traverse case dog: Dog => Some(dog); case _ => None
或者你可以使用你自己的 Typeclass 来概括它 trait Is[T, U] def check(t: T): Option[U]
最终会做相同的match
并且可以与相同的traverse
一起使用,但它似乎 *Shapeless 已经为你解决了 :)以上是关于将 Scala Iterable[T] 转换为 Option[Iterable[S]],如果所有 T 都是 S 的实例,则填充?的主要内容,如果未能解决你的问题,请参考以下文章
在 Scala 中,从初始对象和生成下一个对象的函数创建 O(1) 内存 Iterable
如何将 Iterable[String] 分组为(键,出现次数)元组