地道的build设,以检查是否订购一个集合

随着学习的意图和进一步的这个问题 ,我一直对一个algorithm检查一个列表(或集合)是否是有序的algorithm的显式recursion的惯用替代好奇。 (我通过使用运算符来比较和Int作为types来保持简单;我想在深入研究它的generics之前先看看这个algorithm)

基本的recursion版本是(by @Luigi Plinge):

def isOrdered(l:List[Int]): Boolean = l match { case Nil => true case x :: Nil => true case x :: xs => x <= xs.head && isOrdered(xs) } 

performance不佳的地道方式是:

 def isOrdered(l: List[Int]) = l == l.sorted 

使用fold的另一种algorithm:

 def isOrdered(l: List[Int]) = l.foldLeft((true, None:Option[Int]))((x,y) => (x._1 && x._2.map(_ <= y).getOrElse(true), Some(y)))._1 

它有一个缺点,就是它会比较列表中的所有n个元素,即使它能在find第一个乱序元素之后再提前停止。 有没有办法“停止”折叠,因此使这是一个更好的解决scheme?

任何其他(优雅)的select?

通过“惯用”,我假设你在他们的文章Applicative Programming With Effects中讨论了McBride和Paterson的“成语”。 :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) 

这是如何工作的:

任何具有Traverse[T]的实现的数据结构T[A]都可以用一个Applicative函子,或者“成语”或者“强壮的幺半群函子”来遍历。 每个Monoid都会自由地Monoid这样一个习语(见本文第四部分)。

monoid只是某种types的关联二元操作,并且是该操作的标识元素。 我定义了一个Semigroup[Lte[A]] (一个半群与一个monoid相同,除了没有标识元素),其关联操作跟踪两个值中的较小者,以及左值是否小于正确值。 当然Option[Lte[A]]就是我们的半群自由生成的幺半群。

最后, foldMapDefault以monoid诱发的习惯用法遍历集合typesT 如果每个值小于以下所有值(表示集合已被sorting),则结果b将包含true;如果T没有元素,则结果b将包含true。 由于空T是按照惯例sorting的,所以我们将其作为第二个parameter passing给Option的最后一个fold

作为奖励,这适用于所有可遍历的集合。 演示:

 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 

一个testing:

 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. 

就是你如何做惯用的遍历来检测一个集合是否是有序的。

这将在第一个失序的元素之后退出。 因此,它应该performance良好,但我没有testing过。 在我看来,这也更加优雅。 🙂

 def sorted(l:List[Int]) = l.view.zip(l.tail).forall(x => x._1 <= x._2) 

事实上,我正在这样做,这跟金斯贝尔很相似。

 def isOrdered(list: List[Int]): Boolean = ( list sliding 2 map { case List(a, b) => () => a < b } forall (_()) ) 

如果您在上面的评论中错过了missfaktor的优雅解决scheme:

 (l, l.tail).zipped.forall(_ <= _) 

这个解决scheme是非常可读的,并将退出第一个乱序元素。

recursion版本是好的,但限于List (有限的变化,这将在LinearSeq上运行LinearSeq )。

如果它是在标准库中实现的(这是有道理的),它可能会在IterableLike完成,并且有一个完全必要的实现(例如,请参阅方法find

你可以用return来中断foldLeft (在这种情况下,你只需要前一个元素,而不是一直布尔)

 import Ordering.Implicits._ def isOrdered[A: Ordering](seq: Seq[A]): Boolean = { if (!seq.isEmpty) seq.tail.foldLeft(seq.head){(previous, current) => if (previous > current) return false; current } true } 

但是我不明白这比命令性的实施更好甚至是惯用的。 我不确定我不会把它称为实际上的必要。

另一个解决scheme可能是

 def isOrdered[A: Ordering](seq: Seq[A]): Boolean = ! seq.sliding(2).exists{s => s.length == 2 && s(0) > s(1)} 

简而言之,也许这可以称为惯用,我不确定。 但是我觉得还不太清楚。 而且,所有这些方法可能会比命令式或尾recursion式更糟糕,而且我不认为它们会有更多的清晰度来购买。

你也应该看看这个问题 。

要停止迭代,可以使用Iteratee :

 import scalaz._ import Scalaz._ import IterV._ import math.Ordering import Ordering.Implicits._ implicit val ListEnumerator = new Enumerator[List] { def apply[E, A](e: List[E], i: IterV[E, A]): IterV[E, A] = e match { case List() => i case x :: xs => i.fold(done = (_, _) => i, cont = k => apply(xs, k(El(x)))) } } def sorted[E: Ordering] : IterV[E, Boolean] = { def step(is: Boolean, e: E)(s: Input[E]): IterV[E, Boolean] = s(el = e2 => if (is && e < e2) Cont(step(is, e2)) else Done(false, EOF[E]), empty = Cont(step(is, e)), eof = Done(is, EOF[E])) def first(s: Input[E]): IterV[E, Boolean] = s(el = e1 => Cont(step(true, e1)), empty = Cont(first), eof = Done(true, EOF[E])) Cont(first) } scala> val s = sorted[Int] s: scalaz.IterV[Int,Boolean] = scalaz.IterV$Cont$$anon$2@5e9132b3 scala> s(List(1,2,3)).run res11: Boolean = true scala> s(List(1,2,3,0)).run res12: Boolean = false 

如果将列表分成两部分,并检查第一部分的最后部分是否低于第二部分的第一部分。 如果是这样,你可以并行检查这两个部分。 这里的原理图,首先没有平行:

 def isOrdered (l: List [Int]): Boolean = l.size/2 match { case 0 => true case m => { val low = l.take (m) val high = l.drop (m) low.last <= high.head && isOrdered (low) && isOrdered (high) } } 

现在与平行,并使用splitAt而不是take/drop

 def isOrdered (l: List[Int]): Boolean = l.size/2 match { case 0 => true case m => { val (low, high) = l.splitAt (m) low.last <= high.head && ! List (low, high).par.exists (x => isOrdered (x) == false) } } 
 def isSorted[A <: Ordered[A]](sequence: List[A]): Boolean = { sequence match { case Nil => true case x::Nil => true case x::y::rest => (x < y) && isSorted(y::rest) } } Explain how it works. 

我的解决scheme与missingfaktor的解决scheme和订购相结合

 def isSorted[T](l: Seq[T])(implicit ord: Ordering[T]) = (l, l.tail).zipped.forall(ord.lt(_, _)) 

你可以使用你自己的比较方法。 例如

 isSorted(dataList)(Ordering.by[Post, Date](_.lastUpdateTime)) 
Interesting Posts