14 functional-programming scala class list filter
我正在学习Scala,因为它很符合我的需求,但我发现很难优雅地构造代码.我处于一种情况,我有一个List x并且想要创建两个Lists:一个包含所有元素,SomeClass一个包含所有不属于的元素SomeClass.
val a = x collect {case y:SomeClass => y}
val b = x filterNot {_.isInstanceOf[SomeClass]}
Run Code Online (Sandbox Code Playgroud)
现在我的代码看起来像那样.然而,它不是非常有效,因为它迭代x两次,代码似乎有点hackish.是否有更好(更优雅)的做事方式?
可以假设SomeClass没有子类.
EDITED
虽然可以使用plain partition,但它会丢失collect问题中保留的类型信息.
可以定义一个partition方法的变体,该变体接受一个函数,该函数使用以下方法返回两种类型之一的值Either:
import collection.mutable.ListBuffer
def partition[X,A,B](xs: List[X])(f: X=>Either[A,B]): (List[A],List[B]) = {
val as = new ListBuffer[A]
val bs = new ListBuffer[B]
for (x <- xs) {
f(x) match {
case Left(a) => as += a
case Right(b) => bs += b
}
}
(as.toList, bs.toList)
}
Run Code Online (Sandbox Code Playgroud)
然后保留类型:
scala> partition(List(1,"two", 3)) {
case i: Int => Left(i)
case x => Right(x)
}
res5: (List[Int], List[Any]) = (List(1, 3),List(two))
Run Code Online (Sandbox Code Playgroud)
当然,可以使用构建器和所有改进的集合来改进解决方案:).
为了完整性我使用简单的旧答案partition:
val (a,b) = x partition { _.isInstanceOf[SomeClass] }
Run Code Online (Sandbox Code Playgroud)
例如:
scala> val x = List(1,2, "three")
x: List[Any] = List(1, 2, three)
scala> val (a,b) = x partition { _.isInstanceOf[Int] }
a: List[Any] = List(1, 2)
b: List[Any] = List(three)
Run Code Online (Sandbox Code Playgroud)
只想扩展mkneissl的答案,使用"更通用"的版本,该版本应该适用于库中的许多不同集合:
scala> import collection._
import collection._
scala> import generic.CanBuildFrom
import generic.CanBuildFrom
scala> def partition[X,A,B,CC[X] <: Traversable[X], To, To2](xs : CC[X])(f : X => Either[A,B])(
| implicit cbf1 : CanBuildFrom[CC[X],A,To], cbf2 : CanBuildFrom[CC[X],B,To2]) : (To, To2) = {
| val left = cbf1()
| val right = cbf2()
| xs.foreach(f(_).fold(left +=, right +=))
| (left.result(), right.result())
| }
partition: [X,A,B,CC[X] <: Traversable[X],To,To2](xs: CC[X])(f: (X) => Either[A,B])(implicit cbf1: scala.collection.generic.CanBuildFrom[CC[X],A,To],implicit cbf2: scala.collection.generic.CanBuildFrom[CC[X],B,To2])(To, To2)
scala> partition(List(1,"two", 3)) {
| case i: Int => Left(i)
| case x => Right(x)
| }
res5: (List[Int], List[Any]) = (List(1, 3),List(two))
scala> partition(Vector(1,"two", 3)) {
| case i: Int => Left(i)
| case x => Right(x)
| }
res6: (scala.collection.immutable.Vector[Int], scala.collection.immutable.Vector[Any]) = (Vector(1, 3),Vector(two))
Run Code Online (Sandbox Code Playgroud)
只需注意一点:分区方法类似,但我们需要捕获几种类型:
X - >集合中项目的原始类型.
A - >左侧分区中的项目类型
B - >右分区中的项目类型
CC - >集合的"特定"类型(Vector,List,Seq等)这必须是更高级的.我们也许可以解决一些类型推断问题(见阿德里安的响应位置:http://suereth.blogspot.com/2010/06/preserving-types-and-differing-subclass.html),但我感觉懒惰;)
To - >左侧的完整集合类型
To2 - >右侧的完整集合类型
最后,搞笑的"CanBuildFrom"隐含的外观和数据是什么让我们来构建特定的类型,如列表或Vector,一般.它们内置于所有核心库集合中.
具有讽刺意味的是,CanBuildFrom魔术的全部原因是正确处理BitSet.因为我需要更高的CC,我们在使用分区时会得到这个有趣的错误消息:
scala> partition(BitSet(1,2, 3)) {
| case i if i % 2 == 0 => Left(i)
| case i if i % 2 == 1 => Right("ODD")
| }
<console>:11: error: type mismatch;
found : scala.collection.BitSet
required: ?CC[ ?X ]
Note that implicit conversions are not applicable because they are ambiguous:
both method any2ArrowAssoc in object Predef of type [A](x: A)ArrowAssoc[A]
and method any2Ensuring in object Predef of type [A](x: A)Ensuring[A]
are possible conversion functions from scala.collection.BitSet to ?CC[ ?X ]
partition(BitSet(1,2, 3)) {
Run Code Online (Sandbox Code Playgroud)
如果需要的话我会把这个开放给有人来修理!我会看看是否可以在更多游戏之后为您提供适用于BitSet的解决方案.
| 归档时间: |
|
| 查看次数: |
5514 次 |
| 最近记录: |