延长"皮条客我的Iterable"选项

时间:2015-07-21 10:47:52

标签: scala functional-programming iterable

最近我在我的代码中找到了几个我第一次收集解决方案的地方,然后只有在解决方案是唯一的时才继续处理它们(解决方案集合只包含一个元素)。以下代码是尝试以更具功能性的方式解决此问题。

  implicit class GetOnlyOne[A](val coll: Iterable[A]) {
    def getonlyone = {
      if (coll.isEmpty) None
      else if (coll.tail.isEmpty) coll.headOption
      else None
    }
  }

该功能可以像:

一样使用
Seq(1).getonlyone
Seq(1,2).getonlyone
Set(1).getonlyone

目前无效的是:

Some(1).getonlyone

除了Option之外,是否可以使用视图边界来改进函数以接受Iterable

2 个答案:

答案 0 :(得分:3)

您可以使用视图边界为Option提供一些东西,但一般的解决方案是定义一个类型类:您定义一个接口并为您想要支持的每种类型提供该接口的隐式实例:

trait CanGetOnlyOne[F[_]] {
  def getOnlyOne[A](fa: F[A]): Option[A]
}
object CanGetOnlyOne {
  implicit object CanGetOnlyOneIterable extends CanGetOnlyOne[Iterable]{
    def getOnlyOne[A](fa: Iterable[A]) = ...
  }
  implicit object CanGetOnlyOneOption extends CanGetOnlyOne[Option] {
    def getOnlyOne[A](fa: Option[A]) = fa
  }
}
implicit class GetOnlyOne[F[_], A](fa: F[A])(implicit cgoo: CanGetOnlyOne[F]) {
  def getonlyone = cgoo.getOnlyOne(fa)
} 

答案 1 :(得分:2)

Option可以隐式转换为Iterable,因此以下内容有效:

implicit class GetOnlyOne[A, Coll](coll: Coll)
                                  (implicit view: Coll => Iterable[A]) {
  def getonlyone = {
    val it: Iterable[A] = coll
    if (it.isEmpty) None
    else if (it.tail.isEmpty) it.headOption
    else None
  }
}

然而,Option效率非常低,因为getonlyone本质上是identity函数。因此,我只是为选项引入第二个方法扩展:

implicit class GetOnlyOneOption[A](private val opt: Option[A]) extends AnyVal {
  def getonlyone = opt
}