地道的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))