通过“惯用语”,我假设您正在谈论 McBride 和 Paterson 在他们的论文Applicative Programming With Effects中的“惯用语” 。:o)
以下是您如何使用他们的习语来检查集合是否已订购:
import scalaz._
import Scalaz._
case class Lte[A](v: A, b: Boolean)
implicit def lteSemigroup[A:Order] = new Semigroup[Lte[A]] {
def append(a1: Lte[A], a2: => Lte[A]) = {
lazy val b = a1.v lte a2.v
Lte(if (!a1.b || b) a1.v else a2.v, a1.b && b && a2.b)
}
}
def isOrdered[T[_]:Traverse, A:Order](ta: T[A]) =
ta.foldMapDefault(x => some(Lte(x, true))).fold(_.b, true)
这是它的工作原理:
任何T[A]
存在 , 实现的数据结构Traverse[T]
都可以用Applicative
函子、或“成语”或“强松散单曲面函子”进行遍历。碰巧everyone 都Monoid
免费引出了这样一个习语(见论文的第4 节)。
幺半群只是某种类型的关联二元运算,以及该运算的标识元素。我正在定义一个Semigroup[Lte[A]]
(半群与幺半群相同,除了没有单位元素),其关联运算跟踪两个值中的较小者以及左值是否小于右值。当然Option[Lte[A]]
,这只是我们的半群自由生成的幺半群。
最后,foldMapDefault
遍历T
由幺半群诱导的成语中的集合类型。b
如果每个值都小于以下所有值(意味着集合已排序),或者没有元素,None
则结果将包含 true。T
由于空T
是按约定排序的,我们将true
作为第二个参数传递fold
给Option
.
作为奖励,这适用于所有可遍历的集合。一个演示:
scala> val b = isOrdered(List(1,3,5,7,123))
b: Boolean = true
scala> val b = isOrdered(Seq(5,7,2,3,6))
b: Boolean = false
scala> val b = isOrdered(Map((2 -> 22, 33 -> 3)))
b: Boolean = true
scala> val b = isOrdered(some("hello"))
b: Boolean = true
一个测试:
import org.scalacheck._
scala> val p = forAll((xs: List[Int]) => (xs /== xs.sorted) ==> !isOrdered(xs))
p:org.scalacheck.Prop = Prop
scala> val q = forAll((xs: List[Int]) => isOrdered(xs.sorted))
q: org.scalacheck.Prop = Prop
scala> p && q check
+ OK, passed 100 tests.
这就是您进行惯用遍历以检测集合是否已排序的方式。