Function innerhalb einer Function?

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

 

PhillipK

Betreff: Function innerhalb einer Function?

BeitragDi, Jul 26, 2011 6:36
Antworten mit Zitat
Benutzer-Profile anzeigen
Heyho!

Ich habe schön öfters mal etwas gesehen, was ich nicht ganz nachvollziehen konnte.

Hier mal Pseudocode:

BlitzMax: [AUSKLAPPEN]
Function Irgendwas:Int(Zahl1:Int, Zahl2:Int)
Local zahl3:Int = Addiere Zahl1,zahl2

Return zahl3*13.37

Function Addiere:Int(a:Int,b:Int)
Return a+b
End Function
End Function


Wieso macht man soetwas? Eine minifunktion innerhalb einer funktion deklarieren?

Der logikhalber ist die Funktion 'Addiere' nur innerhalb von 'Irgendwas' aufrufbar.
Aber wieso trägt man an dieser stelle nicht direkt den code ein?

Ist das wie eine Art sprungmarke zu verstehen?

Klar, jetzt wo ich weiß, das das möglich ist, kann es teilweise schon sinn ergeben. Aber manchmal sehe ich das ganze auch bei 'nur' einem aufruf. Hat das sonstige vorteile, die ich grade nicht erahne?

Wie siehts mit den geschwindigkeiten aus? Geschieht im programm hinterher wirklich ein 'sprung' in die funktion oder ersetzt der compiler das durch die eigentliche funktion, die dahinter steht?

Fragen über fragen Very Happy

Gruß, Phillipk Smile

M0rgenstern

BeitragDi, Jul 26, 2011 9:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey.
Ich hab das jetzt mal ausgetestet weil ich mir nicht vorstellen konnte, dass der compiler das überhaupt mitmacht.
Tut er aber.
Führ mal meinen Code so wie er ist aus und dann sorg dafür, dass der Code, der momentan auskommentiert ist (3. letzte Zeile), auch mit ausgeführt wird.
Dann siehst du den Sinn der Sache.

BlitzMax: [AUSKLAPPEN]
SuperStrict

Function Me:Int()
Local x:Int, y:Int
x = 5
y = 6

Local z:Int = Addiere(x, y)

Function Addiere:Int(a:Int, b:Int)
Return a + b
End Function

Return z * z
End Function

Local Erg:Int = Me()

'Local Erg2:Int = Addiere(4, 5)

DebugLog(Erg)
DebugLog(Erg2)


Der Sinn ist folgender: Der Scope der Funktion Addiere(a:int, b:int) liegt nur innerhalb der Funktion Me().
Damit kann man Funktionen kurzzeitib überschreiben.
Beispiel: Du hast eine Funktion die dir irgendeine Zahl berechnet. Diese Funktion macht das aber normalerweise nur mit Integern und gibt diese auch nur zurück. Du brauchst diese Funktion ganz oft in deinem Programm, also ist sie nötig. Jetzt brauchst du diese Funktion in einer einzigen anderen Funktion aber mit genaueren Ergebnissen, also mit Floats.
Anstatt eine neue Funktion extern anzulegen kannst du in dieser einen Funktion die andere Funktion überschreiben.
Dann wird nur dort mit Floats gerechnet und überall sonst weiterhin mit Integern.

Ich hoffe, ich habe mich einigermaßen verständlich ausgedrückt.

Lg, M0rgenstern
 

PhillipK

BeitragDi, Jul 26, 2011 11:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Mhh. Kurzeitiges "überschreiben" - klingt auch nach etwas.
Spontan fällt mir in meiner jetzigen programmierlaufbahn zwar nichts ein, wo das von nöten gewesen wäre (nur das dauerhafte überschreiben von zb blitzmax internem Print *g*)..

Nun frag ich mich allerdings, warum ich dann in dieser einen funktion, wo ich dieses eine mal etwas anderes brauche, dann trotzdem den selben namen wählen sollte?
Wenn ich jetzt 50x pro schleifendurchlauf die funktion 'Addiere' brauche, welche nur 2 parameter braucht und einmal die funktion 'Addiere' brauche, welche 3 parameter schluckt.. (so als beispiel).
Warum nenne ich dann Addiere mit 3 parametern nicht einfach Addiere3P ?

Das einzige was ich mir jetzt ungetestet vorstellen kann:

Ich habe 4 funktionen:
Test1, Test2, Addiere und MacheEtwas()

Test1 und Test2 rufen beide MacheEtwas() auf, worin addiere ausgeführt wird.
allerdings hat Test2 Addiere() überladen -> Führt MacheEtwas() nun die "Globale" addiere-funktion aus, oder die Addiere funktion aus dem scope von Test2?

Edit:

Habs grade nachgebaut, nein, klappt nicht ^^

Nunja, über die sinnigkeit lässt sich streiten.
Was mich persöhnlich noch interessiert ist, was der compiler an dieser stelle tut.

Das ganze habe ich zwar schon öfters gesehen, aber gewundert hats mich zuerst in diesem Worklog
Dort steht beschrieben, wie die Abfrage ob die maus innerhalb des Hexagons liegt, geschrieben wurde. Samt beispielcode.
Darin sieht es wirklich so aus, das diese Funktion-in-funktions-deklarierung nur für kurzlebigen gebrauch gedacht ist.

Nunja. Was macht der compiler?
wenn er tatsächlich die selbe zeit benötigt, wie bei einem normalen funktionsaufruf, würde ich es vermeiden.
Wenn er allerdings diese kurzlebige funktion direkt wie eine Const zb mit den werten etc ersetz (dh ohne sprung arbeitet), dann werde ich mir diesen stil wohl angewöhnen :>
  • Zuletzt bearbeitet von PhillipK am Di, Jul 26, 2011 11:24, insgesamt einmal bearbeitet

FireballFlame

BeitragDi, Jul 26, 2011 11:17
Antworten mit Zitat
Benutzer-Profile anzeigen
M0rgenstern hat Folgendes geschrieben:
Du hast eine Funktion die dir irgendeine Zahl berechnet. Diese Funktion macht das aber normalerweise nur mit Integern und gibt diese auch nur zurück. Du brauchst diese Funktion ganz oft in deinem Programm, also ist sie nötig. Jetzt brauchst du diese Funktion in einer einzigen anderen Funktion aber mit genaueren Ergebnissen, also mit Floats.
Anstatt eine neue Funktion extern anzulegen kannst du in dieser einen Funktion die andere Funktion überschreiben.
Dann wird nur dort mit Floats gerechnet und überall sonst weiterhin mit Integern.

Das halte ich für keine so gute Idee. Wenn die neue Funktion fast das gleiche macht wie die alte (sonst sollte man sie anders nennen), sollte man sie auch neben der alten deklarieren. Auch zwecks Wiederverwertbarkeit: wenn man die Int-Variante so oft braucht, kann man doch kaum wissen, dass man die Float-Variante nie wieder benötigt, oder?
Ich würde eher sagen, man benutzt es für sehr spezifische Funktionen, die wirklich nur innerhalb der anderen Funktion Sinn ergeben (kommt selten vor). Selbst wenn man sie dann nur einmal aufruft, kann es zumindest den Code übersichtlicher machen.


PhillipK hat Folgendes geschrieben:
Test1 und Test2 rufen beide MacheEtwas() auf, worin addiere ausgeführt wird.
allerdings hat Test2 Addiere() überladen -> Führt MacheEtwas() nun die "Globale" addiere-funktion aus, oder die Addiere funktion aus dem scope von Test2?

Die globale. Die 2. Addiere-Funktion gibt es ja schließlich nur in Test2, MacheEtwas kennt die nicht.
PC: Intel Core i7 @ 4x2.93GHz | 6 GB RAM | Nvidia GeForce GT 440 | Desktop 2x1280x1024px | Windows 7 Professional 64bit
Laptop: Intel Core i7 @ 4x2.00GHz | 8 GB RAM | Nvidia GeForce GT 540M | Desktop 1366x768px | Windows 7 Home Premium 64bit
  • Zuletzt bearbeitet von FireballFlame am Di, Jul 26, 2011 15:39, insgesamt 2-mal bearbeitet

M0rgenstern

BeitragDi, Jul 26, 2011 11:29
Antworten mit Zitat
Benutzer-Profile anzeigen
@FireballFlame,
Das war irgendwie das einzig logische das mir irgendwie dazu eingefallen ist. Ich wüsste nämlich kein wirkliches Anwendungsbeispiel bei dem man sowas bräuchte. Ich finde das irgendwie ziemlich unnötig ne Funktion in ner Funktion erstellen zu können.

Lg, M0rgenstern

Midimaster

BeitragDi, Jul 26, 2011 11:58
Antworten mit Zitat
Benutzer-Profile anzeigen
Das ganze erinnert ein wenig an Public/Privat Deklarierungen von Funktionen in Klassen.

Hier kann ich Funktionen in einer Klasse so abschotten, dass Sie nur innerhalb der Klasse aufgerufen werden können.

Das ist bei Klassen schon sehr hilfreich, weil man nach Jahren noch weiß, ob eine bestimmte Funktion Kontakt zur Außenwelt hat, oder beim Überarbeiten der Klasse nach Belieben verändert werden darf: Es gibt ja garantiert keine externe Code-Stelle, die sich auf die Existenz dieser Funktion in der alten Form verlässt.

Hier sähe ich auch eine sinnvolle Anwendung für die "Funktion in Funktion" wie ihr sie heute entdeckt habt.

FireballFlame

BeitragDi, Jul 26, 2011 12:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Da man sowas machen kann
BlitzMax: [AUSKLAPPEN]
SuperStrict
Framework BRL.StandardIO


Test
End


Function Test()
Function Blub()
Print "blub"
End Function

Bla Blub
End Function


Function Bla(x())
x
End Function

haben sie vielleicht ähnlichen Nutzen wie Lambda Expressions in anderen Sprachen.
Von denen hab ich allerdings nicht viel Ahnung, kennt sich hier jemand damit aus? Razz
PC: Intel Core i7 @ 4x2.93GHz | 6 GB RAM | Nvidia GeForce GT 440 | Desktop 2x1280x1024px | Windows 7 Professional 64bit
Laptop: Intel Core i7 @ 4x2.00GHz | 8 GB RAM | Nvidia GeForce GT 540M | Desktop 1366x768px | Windows 7 Home Premium 64bit
 

PhillipK

BeitragDi, Jul 26, 2011 12:23
Antworten mit Zitat
Benutzer-Profile anzeigen
FireballFlame hat Folgendes geschrieben:
Da man sowas machen kann
BlitzMax: [AUSKLAPPEN]
SuperStrict
Framework BRL.StandardIO


Test
End


Function Test()
Function Blub()
Print "blub"
End Function

Bla Blub
End Function


Function Bla(x())
x
End Function

haben sie vielleicht ähnlichen Nutzen wie Lambda Expressions in anderen Sprachen.
Von denen hab ich allerdings nicht viel Ahnung, kennt sich hier jemand damit aus? Razz


Oh gott. Noch viel zu lernen ich hab, ich junger padawan^^

Kann mir jemand gleich noch ein beispiel nennen, wo es sinnvoll ist, einen funktionspointer an eine funktion zu übergeben? (zumal ich nichtmal wusste, das das geht! war schon ganz überrascht, als ich rausfand, das man funktionspointer als field in einem Type ablegen und ausführen kann^^ was enorm if's sparen kann Wink )

ZEVS

Betreff: Lambda Funktionen

BeitragDi, Jul 26, 2011 12:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Lambda-Funktionen kenne ich aus JavaScript, wo die eher inflationär verwendet werden. Ein Anwendungsgebiet wären z.B. sog. Event-Listener:
BlitzMax: [AUSKLAPPEN]
Type vehicle
Field onLowFuel() 'Event-Listener; wird ausgeführt, wenn der Tank fast leer ist (fuel < 2)
Field fuel:Int
End Type

Global PKW:vehicle = New vehicle
PKW\onLowFuel = alertDriver
PWK\fuel = 10

Function alertDriver()
Print "LOW FUEL!"
End Function

Repeat
PWK\fuel = PKW\fuel-1
If PKW\fuel < 2 Then handleEvent PKW\onLowFuel
Until PKW\fuel <= 0

Print "no fuel"

Function handleEvent(func())
If func Then func
End Function


Ich hoffe mal, das stimmt so (habe keine Ahnung von bMax)

FireballFlame

BeitragDi, Jul 26, 2011 12:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Das Event-System der GUI-Lib wxMax funktioniert z.B. so. Du übergibst das Event, und die Funktion, die es auslösen soll an eine Connect-Funktion, und den Rest erledigt die Lib.
Beispiel: Beim Schließen des Programmfensters soll eine Sicherheitsabfrage kommen: BlitzMax: [AUSKLAPPEN]
Function OnClose(Event:wxEvent)
If wxMessageBox("Exit program?",,wxICON_QUESTION|wxYES_NO)=wxYES Then End
End Function

ConnectAny(wxEVT_CLOSE_WINDOW,OnClose)


Oder stell dir vor, du hast eine Funktion, die z.B. irgendwo ein Array sortieren muss, aber der Sortieralgorithmus soll je nach Anwendungsfall austauschbar sein. Dann kannst du die Sortierfunktion einfach als Parameter übergeben.
PC: Intel Core i7 @ 4x2.93GHz | 6 GB RAM | Nvidia GeForce GT 440 | Desktop 2x1280x1024px | Windows 7 Professional 64bit
Laptop: Intel Core i7 @ 4x2.00GHz | 8 GB RAM | Nvidia GeForce GT 540M | Desktop 1366x768px | Windows 7 Home Premium 64bit
  • Zuletzt bearbeitet von FireballFlame am Di, Jul 26, 2011 12:47, insgesamt 2-mal bearbeitet

M0rgenstern

BeitragDi, Jul 26, 2011 12:43
Antworten mit Zitat
Benutzer-Profile anzeigen
@ ZEVS:
Das ganze sieht genial aus. Aber irgendwie werde ich das Gefühl nicht los, dass das irgendwie doppelts gemoppelt ist. Über die gleiche Funktion nochmal 2 andere Funktionen aufrufen obwohl eine völlig ausreichen würde.
Vielleicht ist dein Beispiel auch einfach nur... unpassend. Denn bei dem was du da schreibst sehe ich allerhöchstens mehr Schreibarbeit obwohl man mit weniger den gleichen Effekt erzielen würde.
Gibts da vielleicht ein Beispiel, das das besser verdeutlichen würde?

Lg, m0rgenstern

FireballFlame

BeitragDi, Jul 26, 2011 12:46
Antworten mit Zitat
Benutzer-Profile anzeigen
M0rgenstern: Hab noch ein simples Beispiel oben reineditiert.

Das Beispiel von ZEVS sieht nur deshalb doppelt gemoppelt aus, weil alles direkt untereinander steht und man den alertDriver-Aufruf ja auch direkt in die Repeat-Schleife einfügen könnte.
Wenn aber nun die Schleife schon in einem Modul steht (so wie bei wxMax) geht das nicht, und deshalb übergibt man die selbstgeschriebene alert-Funktion dann als Parameter.
PC: Intel Core i7 @ 4x2.93GHz | 6 GB RAM | Nvidia GeForce GT 440 | Desktop 2x1280x1024px | Windows 7 Professional 64bit
Laptop: Intel Core i7 @ 4x2.00GHz | 8 GB RAM | Nvidia GeForce GT 540M | Desktop 1366x768px | Windows 7 Home Premium 64bit
  • Zuletzt bearbeitet von FireballFlame am Di, Jul 26, 2011 12:55, insgesamt 5-mal bearbeitet

ZEVS

Betreff: größere Event-Struktur

BeitragDi, Jul 26, 2011 12:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Mit der Vergrößerung der Event-Struktur könnte das Beispiel etwas mehr Sinn ergeben, auch wenn FireballFlame wohl wesentlich bessere hat:
BlitzMax: [AUSKLAPPEN]
Type vehicle
Field onLowFuel() 'Event-Listener; wird ausgeführt, wenn der Tank fast leer ist (fuel < 2)
Field fuel:Int
End Type

Type human
Field onBeHit()'Wenn der Mensch geschlagen wird...
End Type

Global PKW:vehicle = New vehicle
PKW\onLowFuel = alertDriver
PWK\fuel = 10

Function alertDriver()
Print "LOW FUEL!"
End Function

Global plaine:vehicle = New vehicle
plaine\onLowFuel = alertPilot
plaine\fuel = 100

Function alertPilot()
Print "@Pilot: you are going to crash down"
End Function

Global ms:human = New human
Global ZEVS:human = New human

ms\onBeHit = hitZEVS

Function hitZEVS()
Print "ZEVS has been hit"
handleEvent ZEVS\onBeHit 'Deswegen die handleEvent-Funktion: der Event-Handler ist leer!
End Function


Repeat
PWK\fuel = PKW\fuel-1
If PKW\fuel < 2 Then handleEvent PKW\onLowFuel
Until PKW\fuel <= 0

Print "PKW stopped"


Repeat
plaine\fuel = plaine\fuel-1
If plaine\fuel < 2 Then handleEvent plaine\onLowFuel
Until plaine\fuel <= 0

Print "Plaine crashed down"

handleEvent ms\onBeHit 'Sorry!

Function handleEvent(func())
If func Then func
End Function


Wieder keine Garantie.
 

PhillipK

BeitragDi, Jul 26, 2011 13:46
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich fasse mal zusammen, was ich gelernt habe (ich denke meine frage ist beantwortet^^)

Function innerhalb einer Function kann verschiedene möglichkeiten bringen, der sinn dahinter ist fraglich. Die idee, das ganze wie eine Private funktion anzusehen scheint dabei am sinnigsten, da diese funktion auch nur dort verfügbar ist.
Ein beispiel hierfür könnten verschiedene GameLoops sein, mehrere funktionen (TitleScreen(), Credits(), Game()) welche eventuell identische Funktionsnamen besitzen, welche aber alle was anderes bewirken. Beispiel wäre eine Quit() funktion.

TitleScreen: Quit beendet das programm ohne nachzufragen, da dies sicher so gewünscht ist.
Credits: Das programm warnt davor, das der bisherige spielstand noch nicht gespeichert ist (es hätte nur via AppTerminatate ausgelöst werden können)
Game: Quit fragt nach, ob man direkt beenden möchte oder ob man zurück zum TitleScreen möchte.

Und funktionspointer als parameter zu übergeben findet seinen sinn zb in EventListener oder in einem Mod (die Netzwerklib die eine gewisse initialisierungsfunktion aufrufen kann, sobald man 'verbunden' ist, kann nichts von meiner Spezifischen initialisierungsfunktion wissen. Diese übergebe ich dann als parameter zum Connect() dazu. ((Wobei das auch wieder ein event wäre, nicht war? OnConnect -> Init() )) )

Alles richtig zusammengefasst? :3

ZEVS

BeitragDi, Jul 26, 2011 14:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Jawoll!
Dein Beispiel ist korrekt, die Quit()-Funktion macht die Fraglichkeit des Sinns gleichbenannter Funktionen sehr gut deutlich.

Xeres

Moderator

BeitragDi, Jul 26, 2011 14:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn sich Code wiederholt, schreibt man eine Funktion. Das kann auch in Funktionen selbst der Fall sein, da hilft es der Übersicht (und beim Debuggen!) ungemein, wenn man nochmal eine Sub-Funktion benutzt. Sucht oder berechnet man einen bestimmten Wert, kann man mit Return sofort zurück in die Hauptfunktion springen, ohne komplizierte Konstruktionen zu bauen, die die Rechnung abbrechen.
Klar kann man mehr Geschwindigkeit herausholen, wenn man später alle Funktionsaufrufe durch ihren Code ersetzt (wie Inline-Funktionen in anderen Sprachen) aber mindestens in der Entwicklung benutze ich sie öfters.

Funktionspointer sind auch super praktisch, wenn man ich auch nicht ständig damit arbeite: Der eingebauten OnEnd() Funktion z.B. gebe ich immer die "Speicher die Optionen/Daten" Funktion(en) an, und muss mir keine weiteren Gedanken drum machen.

Behaltet die Möglichkeiten im Hinterkopf Wink
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group