「慣用的な」とは、マクブライドとパターソンの論文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
たまたま、every がそのMonoid
ようなイディオムを無料で誘導することがあります (論文のセクション 4 を参照)。
モノイドは、ある型に対する連想二項演算であり、その演算の恒等要素です。Semigroup[Lte[A]]
連想演算が 2 つの値のうち小さい方を追跡し、左の値が右の値より小さいかどうかを追跡する (セミグループはモノイドと同じですが、恒等要素がないことを除いて) を定義しています。そしてもちろんOption[Lte[A]]
、半群によって自由に生成されたモノイドです。
最後に、モノイドによって誘導されたイディオムfoldMapDefault
のコレクション型をトラバースします。各値が次のすべての値より小さかった場合 (つまり、コレクションが順序付けられていた場合)、または に要素がなかった場合、T
結果には true が含まれます。空は規則に従ってソートされるため、2 番目の引数として の最終引数に渡します。b
None
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.
そして、コレクションが順序付けされているかどうかを検出するために慣用的なトラバーサルを行う方法です。