为了学习并进一步了解question,我仍然对用于检查列表(或集合)是否被排序的算法的显式递归的惯用替代品感到好奇。 (我在这里通过使用运算符进行比较和Int作为类型来保持简单;我想在深入研究它的泛型之前先查看算法)
基本的递归版本(由@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)
}
表现不佳的惯用方法是:
def isOrdered(l: List[Int]) = l == l.sorted
使用fold的替代算法:
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个元素,即使它在找到第一个无序元素之后可以提前停止。有没有办法“停止”折叠,从而使其成为更好的解决方案?
任何其他(优雅)替代品?
答案 0 :(得分:63)
这将在第一个无序的元素之后退出。因此它应该表现良好,但我还没有测试过。在我看来,它也更加优雅。 :)
def sorted(l:List[Int]) = l.view.zip(l.tail).forall(x => x._1 <= x._2)
答案 1 :(得分:38)
通过“惯用语”,我假设你在他们的论文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
仿函数或“惯用法”或“强疏松的monoidal仿函数”遍历。事实恰恰相反,每个Monoid
都会免费引入这样一个成语(见本文第4节)。
monoid只是某种类型的关联二元运算,以及该运算的标识元素。我正在定义Semigroup[Lte[A]]
(半群与monoid相同,除了没有identity元素),其关联操作跟踪两个值中的较小值以及左值是否小于正确值。当然Option[Lte[A]]
只是我们半群自由生成的幺半群。
最后,foldMapDefault
遍历由monoid引起的习语中的集合类型T
。如果每个值小于以下所有值(表示集合已订购),则结果b
将包含true;如果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.
测试:
{{1}}
并且 如何执行惯用遍历以检测集合是否已订购。
答案 2 :(得分:8)
我正在使用这个,这与Kim Stebel非常相似,事实上。
def isOrdered(list: List[Int]): Boolean = (
list
sliding 2
map {
case List(a, b) => () => a < b
}
forall (_())
)
答案 3 :(得分:5)
答案 4 :(得分:2)
递归版本很好,但仅限于List
(如果更改有限,它会在LinearSeq
上运行良好。)
如果它是在标准库中实现的(有意义的话),它可能会在IterableLike
中完成并且具有完全必要的实现(参见例如方法find
)
您可以使用foldLeft
中断return
(在这种情况下,您只需要前一个元素而不是布尔值)
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
}
但是我没有看到它比命令式实现更好或甚至是惯用语。我不确定实际上我不会称之为必要。
另一种解决方案可能是
def isOrdered[A: Ordering](seq: Seq[A]): Boolean =
! seq.sliding(2).exists{s => s.length == 2 && s(0) > s(1)}
相当简洁,也许这可以被称为惯用,我不确定。但我认为这不太清楚。而且,所有这些方法可能会比命令式或尾部递归版本执行得更糟,我认为它们没有任何额外的清晰度可以购买。
另外,您应该查看this question。
答案 5 :(得分:1)
要停止迭代,您可以使用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
答案 6 :(得分:0)
如果将List拆分为两部分,并检查第一部分的最后一部分是否低于第二部分的第一部分。如果是这样,您可以检查两个部分的并行。这里是原理图,首先是不平行的:
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)
}
}
答案 7 :(得分:0)
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.
答案 8 :(得分:0)
我的解决方案结合了missingfaktor的解决方案和订购
def isSorted[T](l: Seq[T])(implicit ord: Ordering[T]) = (l, l.tail).zipped.forall(ord.lt(_, _))
您可以使用自己的比较方法。 E.g。
isSorted(dataList)(Ordering.by[Post, Date](_.lastUpdateTime))