Scala 使用cats创建抽象集合的有效方法
我有一些用于文件流处理的代码。为了测试这段代码,我希望在Scala 使用cats创建抽象集合的有效方法,scala,scala-cats,monix,Scala,Scala Cats,Monix,我有一些用于文件流处理的代码。为了测试这段代码,我希望在可观察的上执行的操作与类型无关,这样我也可以在任何其他数据结构(如列表)上执行它们。这就是我编写以下代码来抽象底层数据结构的原因: def permutations[F[_] : Applicative : FunctorFilter : SemigroupK](chars: F[Char]): F[F[Char]] = { Range.inclusive('a', 'z').map(_.toChar) .map { c ⇒
可观察的
上执行的操作与类型无关,这样我也可以在任何其他数据结构(如列表
)上执行它们。这就是我编写以下代码来抽象底层数据结构的原因:
def permutations[F[_] : Applicative : FunctorFilter : SemigroupK](chars: F[Char]): F[F[Char]] = {
Range.inclusive('a', 'z').map(_.toChar)
.map { c ⇒
FunctorFilter[F].filter(chars)(Character.toLowerCase _ andThen (_ != c))
}
.map(Applicative[F].pure)
.reduceLeft(SemigroupK[F].combineK)
}
让我头疼的是,这段代码创建了很多中间数据结构。我是否可以使用一个类型类来提高这个过程的效率?将一个数据结构提升到另一个数据结构而不需要太多开销的东西,比如但对于项集合 看来猫咪对此没有任何好处。monix并不是更好,它只实现了cats中的少数类型类 因此,我最好的猜测是自己定义这样的类型类:
import monix.execution.Scheduler.Implicits.global
import cats._
import cats.implicits._
import monix.reactive._
object Test {
def main(args: Array[String]): Unit = {
println(permutations(List('a', 'b', 'c')))
permutations(Observable('a', 'b', 'c')).foreach{c =>
print("Observable(")
c.foreach(c1 => print(c1 + " "))
print(") ")
}
}
def permutations[F[_] : Applicative](chars: F[Char])(implicit seq: Sequence[F], fil: Filter[F]): F[F[Char]] = {
val abc = seq.fromIterable(
Range.inclusive('a', 'z').map(_.toChar)
)
abc.map(c => fil.filter(chars)(_ != c))
}
trait Sequence[F[_]] {
def fromIterable[A](f: Iterable[A]): F[A]
}
implicit val listSequence: Sequence[List] = new Sequence[List] {
def fromIterable[A](f: Iterable[A]): List[A] = f.toList
}
implicit val observableSequence: Sequence[Observable] = new Sequence[Observable] {
def fromIterable[A](f: Iterable[A]): Observable[A] = Observable.fromIterable(f)
}
trait Filter[F[_]] {
def filter[A](fa: F[A])(f: A => Boolean): F[A]
}
implicit val observableFilterFunctor: Filter[Observable] = new Filter[Observable] {
def filter[A](fa: Observable[A])(f: A => Boolean): Observable[A] =
fa.filter(f)
}
implicit val listFilterFunctor: Filter[List] = new Filter[List] {
def filter[A](fa: List[A])(f: A => Boolean): List[A] =
fa.filter(f)
}
}
结果:
List(List(b, c), List(a, c), List(a, b), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c), List(a, b, c))
Observable(b c ) Observable(a c ) Observable(a b ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c ) Observable(a b c )
很遗憾,我无法在scalafiddle或scastie上实现这一点,因为两者都没有提供正确的CAT(1.5.0
)和monix(3.0.0-M3
)版本
我仍然希望这能有所帮助。尽管创建可重用函数很有用,但您可以轻松地测试
可观察的
我建议将逻辑处理和副作用消费者分开
object StreamProcessing {
def processItems(obs: Observable[Input]): Observable[Result] = ???
}
在prod中,您可以这样做
val eventsStream: Observable[Input] = ???
val eventsConsumer: Consumer[Input, Output] = ???
StreamProcessing(myEventsStream).consumeWith(eventsConsumer)
然后,在测试中,您可以模拟测试数据,断言列表结果。
此外,通过测试Observable,您可以使用TestScheduler
控制时间,这使得测试变得轻而易举
implicit val sc = TestScheduler()
val testData: List[Input] = ???
val expected: List[Output] = ???
val res = StreamProcessing(Observable.fromIterable(testData))
.toListL
.runToFuture
sc.tick()
assert(res.value, Some(Success(expected))
请问您为什么不使用Observable.fromIterable(…)
进行测试?这不会缓解您当前方法可能存在的任何性能问题吗?@MarkusAppel它可能会,但这也意味着我的测试代码必须开始使用这些效果类型,如Observable
和Task
,这正是我试图避免的。由于数据量巨大,在生产中使用可观察的是必要的,但在测试中,只有少数情况下需要列表就足够了。公平点。请注意,这种泛型编程总是会带来性能影响。所谓中间结构,您是指所需的隐式实例(Applicative
、FunctorFilter
,等等),还是指在不同转换之间创建的隐式实例?如果是后者,则使用两个连续的映射
操作将始终在列表
上执行两次传递(monix和其他人能够对其进行优化)。另一方面,它看起来像是你展开一个集合,然后再将其折叠回去。您可能对递归感兴趣Schemes@AlonsoDominguez后者。我关心的是将创建和组合的所有单个元素List
s。这可能会增加很多开销。