Wie wäre ein funktioneller Ansatz zum Verschieben bestimmter Array-Elemente?

7

Ich habe eine Scala-App mit einer Liste von Elementen mit Kontrollkästchen, so dass der Benutzer einige auswählt und auf eine Schaltfläche klickt, um sie um eine Position nach oben zu verschieben (links). Ich entschied mich, eine Funktion zu schreiben, um Elemente eines beliebigen Typs zu verschieben, die einem bestimmten Prädikat entsprechen. Also, wenn Sie diese Elemente haben:

%Vor%

und das Prädikat ist "Großbuchstaben", die Funktion würde dies zurückgeben:

%Vor%

Kurz gesagt, jede Folge zusammenhängender Elemente, die das Prädikat erfüllen, wird mit dem einzelnen Element links davon getauscht.

Ich habe die folgende hässliche imperative Implementierung gefunden. Ich würde gerne eine schöne und hoffentlich lesbare, funktionale Lösung sehen.

%Vor%

EDIT: Danke allen, ich hoffe euch hat die Übung gefallen!

    
Germán 11.02.2010, 14:06
quelle

9 Antworten

12

Hier ist eine rein funktionale Implementierung

%Vor%

Und hier ist eine, die Mutabilität auf der Innenseite verwendet, um schneller zu sein

%Vor%     
Geoff Reedy 11.02.2010, 15:23
quelle
6

Das könntest du wahrscheinlich über foldLeft (auch bekannt als /: ) tun:

%Vor%

Es muss mit der leeren Zeichenfolge gearbeitet werden, aber:

%Vor%

Ich lasse es als eine Übung, wie dies in die Array-basierte Lösung

geändert werden     
oxbow_lakes 11.02.2010 14:59
quelle
2

Dies ist im Grunde ein imperativer Algorithmus mit einem funktionalen Stil.

%Vor%

Ändert das Array an Ort und Stelle, mit einem Nebeneffekt. Ich denke, es ist wirklich wie die Version in der Frage, außer es ist einfacher zu lesen. Imperativ muss nicht hässlich sein ...

Bearbeiten: verdammt, konnte nicht einschlafen, bis ich dies niedergeschrieben habe (wie oben, nur kompakter):

%Vor%     
huynhjl 12.02.2010 07:57
quelle
1

Ich beanspruche dieses Zeug nicht, um effizient oder lesbar zu sein. Leider scheinen all die guten Antworten zu sein, also bin ich auf Originalität bedacht. : -)

%Vor%

Also, hier ist was passiert. Zuerst verknüpfe ich jedes Zeichen mit seinem Index ( a.zipWithIndex ) und dann getrennt in areP und notP abhängig davon, ob das Zeichen p erfüllt oder nicht.

An diesem Punkt habe ich zwei Sequenzen, die jeweils aus einem Zeichen und seinem Index in der ursprünglichen Sequenz bestehen.

Als nächstes verschiebe ich einfach den Index der Elemente in der ersten Sequenz, indem ich 1 subtrahiere und shifted berechne.

Die Berechnung des neuen Index der unverschobenen Elemente ist viel schwieriger. Für jedes dieser Elemente ( notP map ) mache ich ein foldLeft . Der Akkumulator der linken Falte ist das Element selbst (immer mit seinem Index). Die Sequenz, die gefaltet wird, ist die Folge von verschobenen Elementen - so kann man sehen, dass ich für jedes unverschobene Element die ganze Folge von verschobenen Elementen durchquere (sehr ineffizient!).

Also vergleichen wir den Index des unverschobenen Elements mit dem Index jedes verschobenen Elements. Wenn sie gleich sind, erhöhen Sie den Index des unverschobenen Elements. Da die Reihenfolge der verschobenen Elemente geordnet ist ( partition ändert die Reihenfolge nicht), wissen wir, dass wir zuerst nach niedrigeren Indizes und dann nach höheren Indizes testen werden, um sicherzustellen, dass ein Index seinen Index um so viel wie erhöht erhöht notwendig.

Damit verbinden wir die beiden Sequenzen, sortieren sie nach ihren neuen Indizes und ordnen sie dann dem Element zu.

    
Daniel C. Sobral 11.02.2010 19:51
quelle
1

Nicht der schnellste, aber nicht auf String beschränkt und mit der gleichen Logik wie @oxbow_lakes

%Vor%     
Patrick 11.02.2010 16:45
quelle
1

Hier ist eine andere Variante von Geoffs Antwort:

%Vor%

Schnell getestet mit

%Vor%

Hier ist Version zwei

%Vor%     
Don Mackenzie 12.02.2010 00:50
quelle
1

Ich weiß nicht genug, um es in Scala zu schreiben, aber dieses Problem ist maßgeschneidert für die Listenfunktionen takeWhile und dropWhile . Die Idee ist, dass Sie die Liste der Elemente in drei Teile aufteilen:

  • Linker Teil, berechnet mit takeWhile , enthält Elemente ganz links, die das Prädikat nicht erfüllen.

  • Der mittlere Teil ist die Gruppe der Elemente, die Sie nach links verschieben möchten, berechnet durch Ablegen der linken Elemente und dann takeWhile den Rest.

  • Der rechte Teil ist alles übrig; dropWhile die mittleren Elemente.

Hier ist es in Haskell:

%Vor%

Und hier ist ein Einzeltest:

%Vor%

Haskell-Programmierer könnten List.break oder List.span verwenden, um Aufrufe von takeWhile und dropWhile zu kombinieren, aber ich bin mir nicht sicher, ob Scala diese Dinge hat. Außerdem sind takeWhile und dropWhile nette sinnvolle Namen, während ich zumindest break und span weniger klar finde.

BEARBEITEN : Fixierter rekursiver Aufruf, um shift p right anstelle von right aufzurufen, um alle Gruppen nach oben zu verschieben.

    
Norman Ramsey 12.02.2010 01:55
quelle
0

Bearbeiten: Dies löst das gestellte Problem nicht wirklich - es löst ein verwandtes, aber anderes Problem (Erhöhung der Priorität markierter Elemente um eins). Ich verlasse es hier jedoch als Referenz.

Hier ist ein "Einzeiler", der die gewünschten Arrays für Scala 2.8 verwendet.

%Vor%

Ich überlasse es dem Leser als Übung, herauszufinden, wie es funktioniert. (Wenn Sie wirklich Generika mit T wollen, wird Ihnen in Scala 2.8 ein GenericArray zur Verfügung gestellt; Sie können das dann tun, wenn Sie ein primitives Java-Array wollen.)

    
Rex Kerr 11.02.2010 23:02
quelle
0

Eine Lösung in J:

%Vor%

Lasst uns das in benannte Stücke zum besseren Verständnis aufteilen. Die letzte Zeichenfolge " abDEcfgIh " ist das Ergebnis der Anwendung einer Funktion auf die Zeichenfolge " abcDEfghI ", die das richtige Argument für die Funktion ist. Das Alphabetepaar stellt das linke Argument der Funktion dar (das ist der Teil, der beginnt "( 4 : ..."). Also könnten wir anstelle des 2-Elemente-Vektors der Boxed Strings jede einzeln benennen:

%Vor%

Nun, da wir zwei Variablen " lc " und " uc " für die Klein- und Großbuchstaben haben, untersuchen wir den Hauptteil der Funktion im Detail. Nimmt man einen logisch kohärenten Block vom rechten Ende, da dies zuerst ausgewertet würde, könnten wir dies wie folgt benennen:

%Vor%

Dies definiert " rmUCshift " als etwas, das ein rechtes und linkes Argument benötigt (das " 4 : " gibt dies an), wobei der Körper in der nächsten Zeile beginnt und bis zum blos schließenden Paren fortfährt. Die Form " 4 : 0 ", gefolgt vom Körper, ist eine Variante der Form " 4 : 'body", die anfangs gezeigt wird. Dieses Verb rmUCshift kann unabhängig wie folgt aufgerufen werden:

%Vor%

Der Aufruf wird um drei Leerzeichen eingerückt und die Ausgabe folgt unmittelbar darauf. Das linke Argument (lc;'') ist ein Vektor mit zwei Elementen, wobei das leere Array als zweites Element angegeben ist, da es in diesem Teil des Codes nicht verwendet wird - wir hätten einen beliebigen Wert nach dem Semikolon verwenden können, aber die beiden einfachen Anführungszeichen sind leicht einzugeben .

Die nächsten zu benennenden Stücke sind diese (Definitionen gefolgt von Beispielen):

%Vor%

Die Kombination dieser benannten Stücke ergibt folgendes:

%Vor%

Die Wiederholung von " x rmUCshift y " ist jedoch unnötig und kann vereinfacht werden, um uns folgendes zu geben:

%Vor%     
DevonMcC 08.08.2011 20:44
quelle

Tags und Links