"... diejenigen, die nicht abgeneigt sind, einen Amateur anzustarren, der öffentlich einen Narren beschuldigt, lassen sie beobachten, wie ich beweise, dass Java und Visual Basic bei der Geburt getrennte Zwillinge sind und C ++ nicht einmal ein entfernter Verwandter."Bruce McKinney "Die Hard Basic Basic"Einführung
Ein ständiges Interesse an funktionalen Programmieransätzen führt derzeit dazu, dass traditionelle Programmiersprachen aktiv funktionale Mittel erwerben. Und obwohl reine funktionale Sprachen immer noch nicht sehr beliebt sind, ist die Funktionalität in Sprachen wie C ++, Java, JavaScript, Python und anderen fest etabliert. Die VBA-Sprache ist jedoch seit vielen Jahren bei einem relativ großen Publikum von Microsoft Office-Benutzern beliebt Diese Sprache enthält praktisch keine funktionalen Werkzeuge.
Versuchen wir, diese Lücke zu schließen. Ich schlage eine vollständige (wenn auch möglicherweise nicht perfekte) Implementierung der von VBA implementierten funktionalen Schnittstellen vor. Die Implementierung kann als Grundlage für spätere Verfeinerungen und Verbesserungen dienen.
Funktionsargumente Problem
Das erste Problem, dem wir auf diesem Weg begegnen werden, ist das Problem, funktionale Argumente an eine Funktion oder Methode zu übergeben. Die VBA-Sprache enthält nicht die entsprechenden Tools (der AddressOf-Operator dient nur zum Übertragen von Adressen an die Windows-API-Funktionen und ist nicht vollständig sicher zu verwenden). Gleiches gilt für die bekannte Methode, Funktionen per Zeiger aufzurufen (G. Magdanurov Visual Basic in der Praxis St. Petersburg: „BHV Petersburg“, 2008). Gehen wir kein Risiko ein - wir verwenden bei der Implementierung nur Standard-Sprachfunktionen und Standardbibliotheken.
Leider hilft hier die PLO nicht viel. Um ein Funktionsobjekt in eine Prozedur oder Funktion zu übertragen, bietet VBA eine Standardmöglichkeit: die erforderliche Funktionalität mit einer Objektshell zu umschließen (ein Objekt erstellen, dessen eine der Methoden die erforderliche Funktionalität sein wird). Ein Objekt kann als Parameter übergeben werden. Dieser Ansatz ist praktikabel, aber sehr umfangreich. Für jede erforderliche Funktionalität müssen Sie eine eigene Klasse und ein Objekt dieser Klasse erstellen.
Es gibt einen anderen Weg, der viel einfacher ist und nicht die Erstellung separater Klassen für jede Funktionalität erfordert.
Angenommen, Sie möchten eine anonyme Funktion an eine bestimmte proc-Prozedur übergeben, die ihr Argument um eins erhöht. Diese Funktion kann wie folgt geschrieben werden:
x -> x+1
Diese Notation der Zuweisung anonymer Funktionen ist mittlerweile fast zum "De-facto-Standard" geworden. Die einzige Möglichkeit, eine solche Funktion an einen Parameter zu übergeben, besteht in der Verwendung einer Zeichenfolgendarstellung:
r=proc(a,b,”x->x+1”)
hier sind a und b gewöhnliche Parameter, und der dritte Parameter ist eine unbenannte Funktion, die sehr klar ist und sich kaum von Einträgen in gängigen Programmiersprachen unterscheidet.
Um eine auf diese Weise definierte anonyme Funktion zu verwenden, müssen Sie sie zunächst in die Standardform der VBA-Funktion bringen. Dadurch wird das folgende Dienstprogramm ausgeführt:
Private Function prepCode(Code As String) As String k% = InStr(Code, "->") parms$ = Trim$(Left$(Code, k% - 1)) body$ = Mid$(Code, k% + 2) If Left$(parms$, 1) <> "(" Then parms$ = "(" + parms$ + ")" If InStr(body$, "self") = 0 Then body$ = ";self=" & body$ & ";" body$ = Replace(body$, ";", vbCrLf) prepCode = "function self" & parms & vbCrLf & body & _ vbCrLf & "end function" End Function
Die Funktion wählt eine Liste von Parametern und den Berechnungskörper aus und bildet dann eine Funktion namens self. In unserem Fall hat die Selbstfunktion die folgende Form:
function self(x) self=x+1 End function
In Übereinstimmung mit der VBA-Syntax wird diese Funktion offensichtlich genau das tun, was die anonyme Funktion hätte tun sollen - den Wert ihres Arguments um 1 erhöhen. Richtig, diese Funktion ist noch keine VBA-Funktion, sondern nur eine Zeile, die den angegebenen Code enthält. Um eine Zeichenfolge in eine Funktion umzuwandeln, können Sie die Standard-Microsoft-Bibliothek „Msscript.ocx“ verwenden. Mit dieser COM-Bibliothek können Sie beliebigen VBA-Code ausführen, der in Zeichenfolgenform angegeben ist. Gehen Sie dazu wie folgt vor:
- Erstellen Sie ein ScriptControl-Objekt
- Rufen Sie die Sprachinstallationsmethode (VBScript) auf.
- Rufen Sie die Methode zum Laden von Funktionen auf.
- Rufen Sie die eval-Methode auf, um den Anruf zu tätigen.
Es sieht alles ungefähr so aus:
Set locEv=new ScriptControl locEv.Language = "VBScript" locEv.AddCode prepCode(“x->x+1”) r=locEv.eval(“self(5)”)
Nach dem Ausführen dieses Codes ist der Wert der Variablen r 6.
Drei Punkte sollten hier gemacht werden:
- Der Hauptteil einer anonymen Funktion kann mehrere Zeilen enthalten. Einzelne Aussagen enden in diesem Fall mit einem Semikolon. Aus dem endgültigen Code werden die Zeichen ";" sind ausgeschlossen. Mit einem mehrzeiligen Text können Sie sehr erweiterte Funktionen in anonymen Funktionen implementieren.
- Die Tatsache, dass die anonyme Funktion "in Wirklichkeit" den Namen "Selbst" hat, gibt einen unerwarteten Bonus - eine anonyme Funktion kann rekursiv sein.
- Da das ScriptControl-Objekt zwei Sprachen unterstützt - VBScript und Jscript - kann die anonyme Funktion (theoretisch) in Jscript geschrieben werden (diejenigen, die dies wünschen, können es versuchen).
Als nächstes wird ein Objektimplementierungsmodell beschrieben.
Objektmodell
Die Basis des Modells sind Objekte zweier Typen: Container und Generator. Das Container-Objekt ist ein Repository eines Arrays beliebiger Größe. Das Generator-Objekt implementiert, wie der Name schon sagt, einen allgemeinen Formulargenerator.
Beide Objekte implementieren die aIter-Schnittstelle, die im Folgenden ausführlicher beschrieben wird. Die Schnittstelle enthält 19 Funktionen:
Für ein Generatorobjekt sind einige Methoden nicht direkt implementiert. Sie müssen zuerst eine bestimmte Anzahl von Werten in einem Container auswählen. Beim Versuch, eine nicht realisierte Methode für einen Generator aufzurufen, wird mit Code 666 ein Fehler generiert. Als Nächstes werden einige Beispiele für die Verwendung der beschriebenen Schnittstellen betrachtet.
Beispiele
Drucken von Fibonacci-fortlaufenden Nummern:
Sub Test_1() Dim fibGen As aIter Set fibGen = New Generator fibGen.Init Array(1, 0), "(c,p)->c+p" For i% = 1 To 50 Debug.Print fibGen.getNext() Next i% End Sub
Hier wird ein Generator mit den Anfangswerten 0 und 1 und einer der Fibonacci-Sequenz entsprechenden Erzeugungsfunktion erzeugt. Als nächstes werden die ersten 50 Zahlen in einer Schleife gedruckt.
Karte und Filter:
Sub Test_2() Dim co As aIter Dim Z As aIter Dim w As aIter Set co = New Container co.Init frange(1, 100) Set Z = co.map("x -> 1.0/x"). _ take(20).filter(" x -> (x>0.3) or (x<=0.1)") iii% = 1 Do While Z.hasNext() Debug.Print iii%; " "; Z.getNext() iii% = iii% + 1 Loop End Sub
Ein Container wird durch eine numerische Sequenz im Bereich von 1 bis 100 erstellt und initialisiert. Als nächstes werden die Zahlen mit der Karte durch die Umkehrung ersetzt. Von diesen werden die einundzwanzigsten genommen. Dann wird diese Population gefiltert und Zahlen größer als 0,3 oder kleiner als 0,1 werden daraus ausgewählt. Das Ergebnis wird in den Behälter zurückgegeben, dessen Zusammensetzung gedruckt wird.
Faltung verwenden:
Sub Test_4() Dim co As aIter Set co = New Container co.Init frange(1, 100) v = co.reduce(0, "(acc,x)->acc+x") Debug.Print v v = co.reduce(1, "(acc,x)->acc*x") Debug.Print v End Sub
Hier wird unter Verwendung der Faltung die Summe und das Produkt von Zahlen von 1 bis 100 betrachtet.
Sub Test_5() Dim co1 As aIter Dim co2 As aIter Dim co3 As aIter Set co1 = New Generator co1.Init Array(123456789), "x -> INT(x/10)" Set co2 = co1.takeWhile(100, "x -> x > 0") Set co3 = co2.map("x -> x mod 10") Debug.Print co3.maximun Debug.Print co3.minimum Debug.Print co3.summa Debug.Print co3.production End Sub
In diesem Beispiel wird der Generator co1 konstruiert, wobei die ursprüngliche Zahl nacheinander durch Grad 10 geteilt wird. Dann werden die Quotienten ausgewählt, bis Null erscheint. Danach wird die resultierende Liste der Quotienten durch die Funktion angezeigt, den Rest der Division durch 10 zu nehmen. Das Ergebnis ist eine Liste der Ziffern der Zahl. Die Liste ist zusammengefasst, er berechnete das Maximum, Minimum und Produkt.
Schlussfolgerungen
Der vorgeschlagene Ansatz ist durchaus praktikabel und kann erfolgreich angewendet werden, um alltägliche Aufgaben eines VBA-Programmierers in einem funktionalen Stil zu lösen. Warum sind wir schlimmer als die Javisten?
Laden Sie
hier Beispiele herunter
Viel Glück !!!