*************************************************************** * * * Kap.8: Vektorwertige Funktionen, die Uebergabetypen * * ByVal und ByRef und Subs mit Argumenten * * * *************************************************************** Es gibt im Prinzip 2 Moeglichkeiten, vektorwertige Funktionen in VBA anzulegen: 1) die Funktion gibt direkt einen Vektor, ein Array zurueck 2) die Funktion hat keinen Rueckgabewert (oder nur eine dummy- Variable als Rueckgabewert) und die Vektoren werden ueber die Funktions-Argumente uebergeben In diesem Zusammenhang ist es nuetzlich, wenn man weiss, dass es in VBA die Uebergabetypen ByVal, by value, und ByRef, by reference, gibt. Schauen wir uns das zunaechst mal an: **************************************** * Die Uebergabetypen ByVal und ByRef * **************************************** Wenn man eine Variable x in einem Sub Test() an eine Funktion uebergibt, und in der Funktion wird dann mit dieser Variablen gerechnet und der Wert von x wird veraendert, dann wird typischer- weise, das ist die default-Einstellung, die identisch ist mit dem Uebergabetyp ByRef, auch der Wert von x im aufrufenden Sub Test() mit veraendert. Wenn man das nicht moechte, muss man diese Variable x an die Funktion mit dem Uebergabetyp ByVal uebergeben: wir legen 3 Test-Funktionen an, die alle dasselbe machen: Function TestF1(a As Double) As Double Dim result As Double result = a ^ 2 a = a + 1 TestF1 = result End Function Function TestF2(ByVal a As Double) As Double Dim result As Double result = a ^ 2 a = a + 1 TestF2 = result End Function Function TestF3(ByRef a As Double) As Double Dim result As Double result = a ^ 2 a = a + 1 TestF3 = result End Function Jetzt rufen wir diese 3 Funktionen in einem Sub Test() auf und schauen uns an, ob sich der an die Funktion uebergebene Wert, das ist in dem folgendem Sub ein x, aendern tut: Sub Test() Dim x as double Dim y as double x = 3 y = TestF1(x) Debug.Print x, y 'das Ausfuehren von TestF1 hat den Wert von x geaendert x = 3 y = TestF2(x) Debug.Print x, y 'das Ausfuehren von TestF2 hat den Wert von x nicht geaendert x = 3 y = TestF3(x) Debug.Print x, y 'das Ausfuehren von TestF3 hat den Wert von x geaendert: 'der Uebergabetyp ByRef ist die Default-Einstellung End Sub ***************************************** * ENDE Uebergabetypen ByVal und ByRef * ***************************************** Fuer Arrays gibt es keinen Uebergabetyp ByVal, Arrays werden also immer by reference, ByRef, uebergeben. Wenn Arrays also innerhalb einer Funktion geaendert werden, dann sind diese Aenderungen also immer automatisch auch in dem Sub wirksam, von dem aus die Funktion aufgerufen wurde. Schauen wir uns das jetzt an: wir wollen eine Benutzer- definierte Funktion in VBA anlegen, die einen Vektor beliebiger Laenge annehmen kann und diesen dann element- weise quadrieren tut. Wie oben bereits gesagt, koennen wir das auf 2 (oder 3, mit dem 2a und b) Arten machen: 1) die Funktion gibt direkt einen Vektor, ein Array zurueck: wir nennen die Funktion vecF 2a) die Funktion hat nur eine dummy-Variable als Rueckgabewert und die Vektoren werden ueber die Funktions-Argumente mit Uebergabetyp ByRef (was anderes ist fuer arrays auch gar nicht moeglich) uebergeben: wir nennen die Funktion scalarF 2b) die Funktion hat keinen Rueckgabewert, nur Argumente. Eine Funktion ohne Rueckgabewert ist per Definition ein Sub, dann also ein Sub mit Argumenten: wir nennen das Sub VecSquared Machen wir jetzt die Implementation: ' man beachte, dass wir den Rueckgabetyp von vecF, ein ' array, nicht in der Function vecF(...)-Zeile, in der ' ersten Zeile, mit angeben tun: Function vecF( x() As Double ) Dim y() As Double Dim nlow As Long Dim nup As Long Dim i As Long nlow = LBound(x) nup = UBound(x) ReDim y(nlow To nup) For i = nlow To nup y(i) = x(i) ^ 2 Next i vecF = y End Function Probieren wir das gleich mal aus: das Hauptprogramm, von dem wir jeweils die Sachen aus 1) und 2a,b) auf- rufen wollen, nennen wir etwa TestMain(): Sub TestMain() ' wir erzeugen einen Vektor mit beliebiger (zufaelliger) ' Laenge zwischen 5 und 10: Dim n As Long n = 5 + 5 * Rnd() ' wird automatisch auf eine ganze Zahl Debug.Print n ' gerundet, wegen Datentyp Long Dim x() As Double ReDim x(1 To n) Dim i As Long For i = 1 To n x(i) = 2 * i ' also x = (2,4,...,2n-2,2n) Next i ' dieses x wollen wir jetzt elementweise quadrieren ' mit Hilfe der Funktionen/Subs aus 1) und 2a,b) : ' Moeglichkeit 1) Dim y1() As Double y1 = vecF(x) ' funktioniert! ' Moeglichkeit 2a) Dim y2() As Double Dim dummy As Double ' ist Skalar dummy = scalarF(x, y2) ' geht auch! ' Moeglichkeit 2b) Dim y3() As Double VecSquared x, y3 ' funktioniert auch ' die folgende Syntax liefert eine Fehlermeldung: VecSquared(x, y3) ' folgendes wiederum funktioniert: Call VecSquared(x, y3) ' und das gibt wieder eine Fehlermeldung: Call VecSquared x, y3 End Sub Der folgende Function-header fuer vecF scheint auch zu funktionieren: Function vecF(x() As Double) As Double() ..alles wie oben.. vecF = y End Function man scheint also array-wertige Funktionen mit "As Double()" deklarieren zu koennen. Allerdings gehen die folgenden Sachen nicht: dim x as double() oder dim x() as double() anstatt des ueblichen dim x() as double also alles ein bischen gewoehnungsbeduerftig.. Wir muessen noch die Funktion scalarF und das Sub VecSquared codieren: Function scalarF( x() As Double, y() As Double ) As Double Dim nlow As Long Dim nup As Long Dim i As Long nlow = LBound(x) nup = UBound(x) ReDim y(nlow To nup) For i = nlow To nup y(i) = x(i) ^ 2 Next i scalarF = 0 End Function Sub VecSquared( x() As Double, y() As Double ) Dim nlow As Long Dim nup As Long Dim i As Long nlow = LBound(x) nup = UBound(x) ReDim y(nlow To nup) For i = nlow To nup y(i) = x(i) ^ 2 Next i End Sub