假设我有两个功能foo: Int => Option[Int]
和bar: Int => Option[Int]
,并利用它们来处理列表:如何编写用orElse返回Option的函数?
val xs: List[Int] = ...
xs flatMap {x => foo(x) orElse bar(x)}
我可以写xs flatMap (foo orElse bar)
,而不是{x => foo(x) orElse bar(x)}
?
我可以使用3D派对库。
假设我有两个功能foo: Int => Option[Int]
和bar: Int => Option[Int]
,并利用它们来处理列表:如何编写用orElse返回Option的函数?
val xs: List[Int] = ...
xs flatMap {x => foo(x) orElse bar(x)}
我可以写xs flatMap (foo orElse bar)
,而不是{x => foo(x) orElse bar(x)}
?
我可以使用3D派对库。
您可以使用结合scalaz的Semigroup
类型类的操作(|+|
)。
的Option
默认半群使用内容的半群,并结合的值,如果这两个值都存在。 因此,要模仿orElse
行为,您需要将Option
包装在Tags.FirstVal
标记中。
为Tags.FirstVal
状态的文档:
类型标签选择一个
scalaz.Semigroup
实例选择第一个操作数追加。
可以使用Tag
的.subst
和.unsubst
方法,包装和一些F[T]
内解开一个类型T
。在我看来,你必须通过类型推断来帮助Scala。
总而言之组合功能如下:
type F[T] = Int => Option[T]
val f = Tags.FirstVal.unsubst(
Tags.FirstVal.subst[F, Int](foo) |+|
Tags.FirstVal.subst[F, Int](bar))
,并与flatMap
使用它,你必须以某种方式使用隐式转换从到Option
到List
。所以flatMap
呼叫可能是这样的:
xs flatMap (f(_))
您也可以使|+|
工作作为orElse
如果重写隐Monoid
实例Option
:
import scalaz._, Scalaz._
implicit val inst: Monoid[Option[Int]] =
Tags.First.unsubst(scalaz.std.option.optionFirst[Int])
val foo: Int => Option[Int] = x => if (x % 2 == 0) Some(x) else None
val bar: Int => Option[Int] = x => if (x % 3 == 0) Some(x) else None
val xs = List(1, 2, 3, 4, 5, 6, 7, 8)
xs.flatMap((foo |+| bar)(_))
据我所知,这样的事情是不是在标准库,但你可以使用implicit class自行添加:
implicit class WithOrElse[T, R](f: T => Option[R]) {
def orElse(g: T => Option[R])(x: T) = f(x) orElse g(x)
}
例子:
val foo: Int => Option[Int] = x => if (x % 2 == 0) Some(x) else None
val bar: Int => Option[Int] = x => if (x % 3 == 0) Some(x) else None
val xs = List(1, 2, 3, 4, 5, 6, 7, 8)
xs flatMap (foo orElse bar)
感谢。它是否可用于'猫'或任何其他3D派对库? – Michael
Foo。打我一分钟左右。 *与*完全相同,而'foo'和'bar'的定义相同。幽灵般的!有一个+1 :) –
看起来这个答案有点不对,因为你想要更多的习惯用法,但我会把它留在这里,因为我相信这是用基本的斯卡拉/猫来完成的。 – Kolmar
答案是好的))这是我正在寻找。 – Michael
@Michael我已经更新了一个方法来使'| + |'像'orElse'一样工作。 – Kolmar