Parameteranzahl einer Funktion prüfen

Programmierung unter AOO/LO (StarBasic, Python, Java, ...)

Moderator: Moderatoren

Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Parameteranzahl einer Funktion prüfen

Beitrag von Stephan »

Hallo,

Einer Funktion sollen beim Aufruf mehrere Parameter mitgegeben werden (z.B. 4), die Funktion ist deshalb mit diesen Parametern deklariert. Es kann jedoch sein das der Code der die Funktion aufruft weniger Parameter übergibt als die Anzahl der Parameter welche in der Funktionsdeklaration stehen. Somit entsteht ein Fehler und die Funktion hält an. Ich behelfe mir bisher prinzipiell so:

Code: Alles auswählen

function meineFunktion (a,b,c,d)
On Error Resume Next
x(1) = a
x(2) = b
x(3) = c
x(4) = d

'...
'weiterer Code
'...
end function
Für 4 Parameter ist dieses Verfahren akzeptabel, aber für eine größere Anzahl umständlich. Gibt es eine bessere Lösung? Gibt es möglicherweise sogar eine Lösung für eine beliebige Anzahl von Parametern?

Hinweis:
so etwas wie meineFunktion (a()) hat mich bisher nicht weitergebracht, da die Parameter a...d selbst spezielle Arrays sind (die Funktion wird von einer Calc-Zelle aus aufgerufen), falls a...d z.B. Zahlen sind funktioniert es ... eine Konstruktion meineFunktion (a(1),a(2),a(3),a(4)) liefert in der Basic-IDE eine Fehlermeldung selbst wenn das Array a global deklariert ist.



Gruß
Stephan
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Beitrag von Stephan »

das wußte ich noch garnicht:
Ich habe mir bisher dadurch geholfen, dass ich die Argumente optional erklärt habe, etwa so
Code:

function meineFunktion (a,optional b,optional c, optional d)


Mit
Code:

If b isMissing then
tuwas
next



habe ich das Fehlen abgefragt.


leider scheint calc in dieser Form keine Kommaeingabe zu akzeptieren (ich muß erst ausgiebiger testen), weil - ich will die Funktion aus einer Zelle heraus aufrufen.

Gruß
Stephan
Charly
*****
Beiträge: 450
Registriert: Di, 20.01.2004 13:14
Wohnort: München

Beitrag von Charly »

Hallo Stephan!

Die Problematik hatte ich bisher nicht, da ich optional nur für Unterprogramme verwendet habe. Hast du schon mal probiert, statt den Kommas Strichpunkte zu verwenden.

Noch eine Berichtigung :

Der Befehl für Ismissing lautet:

If Ismissing(b) then
Tuwas
end if

oder auch
If Not ismissing(b) then
Tu was
end if

wenn ich nach einem vorhandenen Argument suche.

Gruß

Charly
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Beitrag von Stephan »

Hallo Charly,

ich habe festgestellt das ich IsMissing im Zusammenhang mit dem Laden von Bibliotheken schon benutzt habe, war mir also doch bekannt.
Meine ursprüngliche Frage war wohl etwas inkonsequent, eigentlich wollte ich wissen:
Gibt es möglicherweise sogar eine Lösung für eine beliebige Anzahl von Parametern?
Es ist möglich z.B. für die eingebaute Funktion Summe eine beliebige Anzahl von Parametern zu vergeben =SUMME(x) wobei x eine Aneinanderreihung einer beliebigen Anzahl von Zellen und Zellbereichen sein kann, jeweils durch ";" getrennt. So etwas benötige ich für eine eigene Funktion. Leider kann ich diese (in beliebiger vorher nicht festliegender Anzahl) nur übergeben, wenn ich sie als String übergebe =meineFunktion("x"). Auf einem Tabellenblatt wird jedoch der Rückgabewert der Funktion nicht automatisch aktualisiert wenn der Parameter als String geschrieben ist. Ich muß also einen Hilfsparameter verwenden und schreiben =meineFunktion(x;"x") hierbei sind beide x gleich was natürlich ziemlich dämlich ist.

Schau mal hier:
viewtopic.php?t=1414&start=15
dafür brauche ich das.

Gruß
Stephan
Charly
*****
Beiträge: 450
Registriert: Di, 20.01.2004 13:14
Wohnort: München

Beitrag von Charly »

Hallo Stephan!


Ich erkenne jetzt dein Problem. Leider kenne ich mich im Ooo nicht so gut aus, als dass ich dir eine Lösung bieten könnte. Als Denkanstoß kann ich nur sagen, wie ich es in Excel lösen würde:

In Excel kann man einem Tabellenblatt oder auch dem ganzen Arbeitsbuch ein Programm hinterlegen, das bei bestimmten Ereignissen startet. Das Ereignis kann z.B. das Wechseln einer Zelle sein. Auf Grund dieses Ereignisses kann ich die Neuberechnung des Tabellenblattes erzwingen, wenn nach Veränderung die Zelle verlassen wird. Auch die Funktionen werden dabei neu ausgeführt.

Der Code in Excel lautet

Code: Alles auswählen

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

Application.CalculateFull

End Sub
Ich weiß nicht, ob es das bei calc auch gibt.

gruß
Charly
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Beitrag von Stephan »

Hallo Charly,

ja die Idee ist gut, ich hatte auch daran gedacht. In calc ist sowas jedoch nicht direkt möglich, so das Du das über ein Makro realisieren must, was eigentlich kein Problem ist: Aber das Makro muß permanent 'im Hintergrund laufen' und ich weiß nichts über die Zielumgebung unter der meine Funktion laufen soll (ich habe die Funktion für einen anderen Forumsteilnehmer geschrieben), so das ich Komplikationen nicht ausschließen kann. Deshalb wollte ich das Vorgehen vermeiden.
Im Prinzip wäre der 'Dokument geändert Status' ein auswertbares Ereignis aber leider gibt es nur die Zustände geändert und nicht geändert, also wenn eine Änderung schon vorliegt erzeugt die zweite Änderung keine Änderung des Status mehr (das geht erst wieder nach dem Speichern).

Ach übrigens habe ich mich mit SUMME() geirrt, doch werden nur maximal 30 Parameter akzeptiert.


Herzlichen Gruß
Stephan
Ramses
**
Beiträge: 21
Registriert: Fr, 16.07.2004 09:20

Beitrag von Ramses »

Würde es nicht reichen, wenn du einen EventListener auf einen bestimmten Range-Bereich richtest?

Und bei der Übergabe von Parametern kannst du doch ebenso ein Array übergeben. Damit bist du recht flexibel was die Anzahl der Parameter anbetrifft.

Ich habe mal folgendes gebraucht:

Code: Alles auswählen

.
.
.
Call BubbleSort(oSheet.getCellRangeByName("SortierBereich")) 'Sortieren mit bubble sort 
.
.
.


Sub BubbleSort(oArrayToSort as Object)
 Dim i, j As Integer
 Dim Temp As Variant

 With oArrayToSort

 For j = .Rows.Count-1 to 0 Step -1      
    For i = 0 to j
         If .getCellByPosition(0,i).Value > .getCellByPosition(0,j).Value Then
             Temp = .getCellByPosition(0,i).Value
             .getCellByPosition(0,i).Value = .getCellByPosition(0,j).Value
             .getCellByPosition(0,j).Value = Temp
         End If
    Next i
 Next j
 
 End With

End Sub

Du siehst, hier ist es auch egal wie viele Cellen zum sortieren angegeben werden. Denn Bereich selber kann man natürlich noch flexibler gestalten - war hier nicht notwendig, daher habe ich einen festen Namens SortierBereich erstellt.

Gruß
Dimi
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Beitrag von Stephan »

Hallo Ramses,
Würde es nicht reichen, wenn du einen EventListener auf einen bestimmten Range-Bereich richtest?
Ja das würde reichen. Genau das meinte ich als ich schrieb:
In calc ist sowas jedoch nicht direkt möglich, so das Du das über ein Makro realisieren must, was eigentlich kein Problem ist: Aber das Makro muß permanent 'im Hintergrund laufen'
(hätte ich den Begriff Listener benutzt hätte das vielleicht Verständnisprobleme verursacht, weshalb ich ganz neutral von Makro sprach) Ich habe auch eigentlich nichts dagegen es so zu realisieren, nur ebend weiß ich nicht was sich neben meiner Funktion in der Calc-Datei noch so abspielt und der Nutzer könnte weitere eigene Funktionen, Makros, ... verwenden die zusammen mit dem Listener zu Problemen führen könnten.

Das hier ist interessant:
Call BubbleSort(oSheet.getCellRangeByName("SortierBereich"))
weil ich noch nicht genau weiß was gemeint ist. Normalerweise sollte das ja wohl heißen, Sortierbereich = z.B. A1:A8, mir kommt aber die Idee das Du einen namentlich auf dem Blatt deklarierten Bereich meinen könntest (obwohl das der weitere Code nur für einen geschlossenen Zellbereich hergibt) und das ist eine interessante Anregung.

Es bleibt aber mein Problem. Ich kann so aufrufen:
=meineFunktion(A1) oder =meineFunktion(A1:B9) oder =meineFunktion("A1;A3:A9;C5:D7")
wenn ich das als String-Parameter übergebe ist eine beliebige Anzahl ebenfalls kein Problem, ich will jedoch eine beliebige Anzahl übergeben ohne einen String zu verwenden, also:
=meineFunktion(A1;A3:A9;C5:D7; ...)


Gruß
Stephan
Ramses
**
Beiträge: 21
Registriert: Fr, 16.07.2004 09:20

Beitrag von Ramses »

Hallo Stephan
weil ich noch nicht genau weiß was gemeint ist. Normalerweise sollte das ja wohl heißen, Sortierbereich = z.B. A1:A8, mir kommt aber die Idee das Du einen namentlich auf dem Blatt deklarierten Bereich meinen könntest
Ja, das ist richtig - da es sich in meinem Fall so angeboten hat. Der Bereich war immer der gleiche und wird sich erstmal nicht ändern.
Es bleibt aber mein Problem. Ich kann so aufrufen:
=meineFunktion(A1) oder =meineFunktion(A1:B9) oder =meineFunktion("A1;A3:A9;C5:D7")
wenn ich das als String-Parameter übergebe ist eine beliebige Anzahl ebenfalls kein Problem, ich will jedoch eine beliebige Anzahl übergeben ohne einen String zu verwenden, also:
=meineFunktion(A1;A3:A9;C5:D7; ...)
Hmm... Also schon wie weiter oben mit der SummenFunktion als Beispiel?!
Wie wäre es denn, wenn du erst eine andere Funktion davor schaltest?
In der holst du dir die markierten Zellen und packst sie in einen Array - der wird dann wiederum an die Hauptfunktion übergeben.

Wobei der Gedanke mit 2 Funktionen sicherlich auch in einer gelöst werden kann.

Aber ich denke, dass du dann doch recht flexibel bist was die Anzahl der Parameter anbetrifft.

Ist nur so ein Gedanke

Gruß

Dimi
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Beitrag von Stephan »

Wie wäre es denn, wenn du erst eine andere Funktion davor schaltest?
In der holst du dir die markierten Zellen und packst sie in einen Array - der wird dann wiederum an die Hauptfunktion übergeben.

Wobei der Gedanke mit 2 Funktionen sicherlich auch in einer gelöst werden kann.

Aber ich denke, dass du dann doch recht flexibel bist was die Anzahl der Parameter anbetrifft.

Ist nur so ein Gedanke

Es tut mir leid, aber das wird nicht gehen. Zunächst einmal es sind keine Zellen markiert, weil ich ja eine Funktion schreibe die auf einem Tabellenblatt so eingetragen wird wie eine normale Funktion, meinetwegen SUMME().
Aber das Hauptproblem ist das es völlig unmöglich ist wenn ich einen Aufruf machen will wie folgt:

=meineFunktion(A1;A3:A9;C5:D7; ...)

irgendetwas aus den Zellen auszulesen außer den momentanem Werten. Enthalten die Zellen z.B. Funktionen kann ich nicht die Funktion auslesen sondern nur den aktuellen Rückgabewert der Funktion. Ich kann bei obenstehenden Funktionsaufruf auch definitiv nicht die Namen oder Koordinaten der Zellen ermitteln, NUR die momentanen Werte. Ein Eintrag A1 oder A3:A9 wird (in dem vorliegenden Fall) immer als Array gewertet und ich kann nur EIN Array zurückgeben, weil es so nicht geht:

function meineFunktion (a(0);a(1);...)
x = irgendetwas
Dim b(x)
For i=0 to irgendetwas
b(x) = a(x)
Next i
'...
End function


da wenn a(0) = A3:A9 ein Array in Array vorliegt und das geht so nicht. (auch Ausdrücke wie A1 werden als 1x1-Arrays aufgefaßt)

Als Beispiel für einen Parameter siehe Funktion "summentest" in Kapitel 10.2 in:
http://www.bcwin.ch/ooo/basic/calc/calc.html



Gruß
Stephan
Charly
*****
Beiträge: 450
Registriert: Di, 20.01.2004 13:14
Wohnort: München

Beitrag von Charly »

Ich habe mir noch eine Lösung deines Problems ausgedacht.
Und zwar durchsuche ich den genutzen Tabellenbereich der aktuellen Tabelle nach der Formel und lese dort die Parameter aus. In meinem Beispiel habe ich mich bei der Suche auf die letzte Zeile beschränkt.

Code: Alles auswählen

Function Ergebnis(Bereich)
AktBuch = ThisComponent
AktZelle = AktBuch.currentSelection
BlattNr = AktZelle.getCellAddress().Sheet
AktBlatt = AktBuch.sheets(BlattNr)

Cursor = AktBlatt.createCursor()
Cursor.gotoEndOfUsedArea (true)
Zeile = Cursor.getRangeAddress().EndRow 
Spalte = Cursor.getRangeAddress().EndColumn
For I = 0 To Spalte 	
	AktZelle = AktBlatt.getCellbyPosition(I,Zeile)
	Inhalt = AktZelle.formula
	Inhalt1 = mid(Inhalt,1,9)      Rem Ziff. 1 um das Ausrufezeichen wegzuschneiden
	
	If Inhalt1 = "=Ergebnis" then
		I= Spalte
	end if	

next
Trz = "("
Argument = split(Inhalt, Trz)
L = Len(Argument(1)) - 1
Parameter = left(Argument(1),L)
Ergebnis = Parameter
End Function
Du hast dann die Parameter in Stringform, mit denen du weiterarbeiten kannst. Wenn du allerdings mehrere Bereiche übergeben willst, brauchst du in der Funktion soviele Dummyvariablen, wie die höchstmögliche Anzahl von Bereichen. Diese kannst du dann allerdings optional erklären. Sie müssen dann nicht belegt werden. Es muss natürlich auch eine Fehlerroutine eingebaut werden, wenn die Formel nicht gefunden wird.



Gruß
Charly
Charly
*****
Beiträge: 450
Registriert: Di, 20.01.2004 13:14
Wohnort: München

Beitrag von Charly »

Hallo!

In meinem obigen Vorschlag ist mir ein Fehler unterlaufen,

Code: Alles auswählen

Statt: If Inhalt1 = "=Ergebnis" then 
muss es heißen If Inhalt1 = "=ERGEBNIS" then 
Denn das Programm wandelt die Funktion immer in Großbuchstaben um.

Allerdings muss noch ein weiterer Denkfehler in meinem Programm sein.
Wenn ich die Funktion =Ergebnis(A8:D8) schreibe, erhalte ich richtig einen Rückgabewert: A8:D8

Speichere ich alles und lade es neu, kommt eine Fehlermeldung:
Fehler beim Laden des Dokuments. Unzulässiger Wert oder Datentyp. Index ausserhalb des definierten Bereichs.
Die Zelle mit der Funktion bleibt leer. Erst wenn ich im überwachten Bereich etwas ändere, kommt wieder die richtige Antwort.

Dafür weis ich keine Lösung.

gruß
Charly
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Beitrag von Stephan »

Hallo Charly,

vielen Dank für die sehr interessante Idee. Das wäre eine Lösung wenn es funktionieren sollte, ich hatte heute jedoch noch keine Zeit etwas zu testen.


Gruß
Stephan
Charly
*****
Beiträge: 450
Registriert: Di, 20.01.2004 13:14
Wohnort: München

Beitrag von Charly »

Hallo!
Speichere ich alles und lade es neu, kommt eine Fehlermeldung:


Fehler beim Laden des Dokuments. Unzulässiger Wert oder Datentyp. Index ausserhalb des definierten Bereichs.



Die Zelle mit der Funktion bleibt leer. Erst wenn ich im überwachten Bereich etwas ändere, kommt wieder die richtige Antwort.
Ich bin jetzt auf den Fehler gekommen. Bei meinem Programm habe die Aktuelle Tabelle über CurrentSelection ermittelt, also die aktuell aktivierte Tabelle. Beim Laden des Dokumentes ist noch keine Tabelle aktiviert, also sagt es Fehler. Mein Programm funktioniert, wenn ich den Anfang anders schreibe:

Code: Alles auswählen

Function Ergebnis(Tabelle, Dummy)
AktBuch = ThisComponent

AktBlatt = AktBuch.sheets.getByName(Tabelle)

Cursor = AktBlatt.createCursor()
Cursor.gotoEndOfUsedArea (true)
und so weiter

Dies hat allerdings den Nachteil, dass ich noch einen Parameter mehr übergeben muss: z.B.: =ERGEBNIS("Test";A1:D8)

Ob es dann noch für deine Zwecke geignet ist, weiß ich nicht.

Gruß

Charly
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Beitrag von Stephan »

Hallo Charly,

Ich denke Dein Vorschlag sollte funktionieren, getestet habe ich es jedoch immer noch nicht.

Das hier:
Dies hat allerdings den Nachteil, dass ich noch einen Parameter mehr übergeben muss: z.B.: =ERGEBNIS("Test";A1:D8 )

ist kein Problem, da ich ja bei Bedarf alle Blätter prüfen kann:

Code: Alles auswählen

'...
myDoc = stardesktop.currentcomponent
Anzahl = myDoc.Sheets.count
For i=0 to Anzahl-1
mySheet = myDoc.Sheets(i)
'hier weiterer Code
Next i
'...
Danke für Deinen Vorschlag.

Gruß
Stephan
Antworten