Idiomatische Konstruktion, um zu überprüfen, ob eine Sammlung ist bestellt
Mit der Absicht zu lernen und weiter zu diesem Frage habe ich blieb neugierig auf das idiomatische alternativen zur expliziten Rekursion für einen Algorithmus, der prüft, ob eine Liste (oder eine Sammlung) bestellt wird. (Ich bin halten die Dinge einfach, hier durch einen operator zu vergleichen und Int als Typ, ich würde gerne betrachten, die der Algorithmus vor dem eintauchen in die Generika davon)
Grundlegende rekursive version wäre (von @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)
}
Einer mangelhaften idiomatischer Weg wäre:
def isOrdered(l: List[Int]) = l == l.sorted
Einen alternativen Algorithmus mit Falten:
def isOrdered(l: List[Int]) =
l.foldLeft((true, None:Option[Int]))((x,y) =>
(x._1 && x._2.map(_ <= y).getOrElse(true), Some(y)))._1
Es hat den Nachteil, dass es vergleicht für alle n Elemente von der Liste, selbst wenn es aufhören könnte früher nach dem Auffinden der ersten out-of-order-element. Gibt es eine Möglichkeit, "stop" Falten und daher macht dies eine bessere Lösung?
Andere (elegante) alternativen?
- Mit Boolean als Werte von
if
Aussagen macht mich traurig, und Luigi ' s version war leicht aus (erkennen umgekehrter Reihenfolge). Fixiert es für Sie. - Ihre "korrigierte" version gibt true zurück, auf
isOrdered(List(1,2,1,2))
, das ist der Grund, warum ich rollte Sie zurück, und das ist, warum ich ändern bin es wieder... - Feste zu beheben.
- Es sollte angemerkt werden, dass
l == l.sorted
funktioniert möglicherweise nicht für Listen von Objekten, die andere als int-Werte, wenn der Sortier-Algorithmus ist nicht stabil.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Durch "Redewendungen", ich nehme an, du redest McBride und Paterson "Idiome" in Ihrem Papier Applicative Programmierung Mit Effekten. :o)
Hier ist, wie Sie verwenden würden, Ihre Idiome zu prüfen, ob eine Sammlung ist bestellt:
Hier ist, wie das funktioniert:
Jede Datenstruktur
T[A]
wo es existiert eine Implementierung vonTraverse[T]
, können befahren werden, die mit einemApplicative
Funktor, oder "idiom", oder "stark lax monoidale functor". Es passiert einfach so, dass jederMonoid
induziert eine solche Ausdrucksweise ist für Sie kostenlos (siehe Abschnitt 4 des Papiers).Ein monoid ist nur eine assoziative binäre operation über einige Typ, und eine identity-element für die Bedienung. Ich bin die Definition einer
Semigroup[Lte[A]]
(eine halbgruppe ist dasselbe wie ein monoid, außer ohne die identity-element), dessen assoziative operation tracks der geringere der beiden Werte, und ob der linke Wert kleiner als der Rechte Wert. Und natürlichOption[Lte[A]]
ist genau das monoid erzeugt, frei von unseren halbgruppe.Schließlich
foldMapDefault
durchläuft die Auflistung TypT
in der Sprache, die durch die monoid. Das Ergebnisb
enthält true, wenn jeder Wert war kleiner als alle folgenden (im Sinne der Sammlung bestellt wurde), oderNone
wenn dieT
hatte keine Elemente. Da eine leereT
sortiert ist, durch Konvention, passieren wirtrue
als zweites argument an die Letztefold
desOption
.Als bonus, das funktioniert für alle passierbar Sammlungen. Eine demo:
Test:
Und das ist, wie Sie idiomatische traversal zu erkennen, ob eine Sammlung bestellt ist.
traverse
wird immer Durchlaufen der ganzen Liste ist, richtig?Traverse
undApplicative
.Hiermit beenden Sie nach dem ersten element, das nicht in Ordnung ist. Es sollten so gut, aber habe ich noch nicht getestet. Es ist auch viel eleganter aus meiner Meinung nach. 🙂
(l, l.tail).zipped.forall(_ <= _)
. 🙂forall
hier.Exception: java.lang.UnsupportedOperationException: tail of empty list
, aber der fix ist Recht einfach:def sorted(l:List[Int]) = l.isEmpty || l.view.zip(l.tail).forall(x => x._1 <= x._2)
l.view.zip(l.tail).toStream.forall(x => x._1 <= x._2)
. Dann den zip-Vorgang nicht durchgeführt werden, die auf die vollständige Liste, wenn es nicht zu früh.def isSorted[T <% Ordered[T]](l: Iterable[T])
Ich werde mit dieser Option, die ist ziemlich ähnlich zu Kim Stebel ist, als eine Angelegenheit von Tatsache.
list.sliding(2).map({ case List(a, b) => a < b }).forall(identity)
wäre etwas effizienter.sliding
gibt einIterator
! Ja, du hast Recht.Im Falle dass Sie verpasste missingfaktor die elegante Lösung in den Kommentaren oben:
Diese Lösung ist sehr gut lesbar und verlassen auf dem ersten out-of-order-element.
Die rekursive version ist in Ordnung, aber beschränkt auf
List
(mit begrenzten änderungen, es würde funktionieren gut aufLinearSeq
).Wenn es umgesetzt wurde in der standard-Bibliothek (würde Sinn machen), würde es wohl getan werden in
IterableLike
und haben eine völlig unerlässlich für die Umsetzung (siehe zum Beispiel Methodefind
)Können Sie unterbrechen den
foldLeft
mit einemreturn
(in diesem Fall benötigen Sie nur den vorhergehenden element und nicht boolean alle zusammen)aber ich weiß nicht, wie es besser ist oder sogar idiomatische als ein Imperativ Umsetzung. Ich bin mir nicht sicher, ich würde nicht nennen es zwingend notwendig eigentlich.
Andere Lösung könnte sein,
Eher knapp, und vielleicht könnte sein genannt Redewendungen, bin ich mir nicht sicher. Aber ich denke, es ist nicht allzu klar. Außerdem, alle diese Methoden würden wahrscheinlich viel schlimmer als die Imperativ-oder tail-rekursive version, und ich glaube nicht, Sie haben keine zusätzlichen Klarheit, die würden es kaufen.
Außerdem sollten Sie einen Blick auf diese Frage.
Beenden die iteration, die Sie verwenden können,Iteratee:
Wenn Sie teilen die Liste in zwei Teile, und prüfen, ob der Letzte der erste Teil ist kleiner als der erste des zweiten Teils. Wenn dem so ist, könnte man check-in parallel für beide Teile. Hier der Schaltplan, Idee, erstmal ohne parallele:
Und jetzt mit parallel-und mit
splitAt
statttake/drop
:List
.meine Lösung kombinieren mit missingfaktor Lösung und Bestellung
und Sie können Ihre eigenen Vergleich Methode. E. g.