Verschiedene Arten von Argumentausdrücken in Java
Operatorausdrücke als Argumentausdrücke
Da ein Argumentausdruck ein ein im Prinzip beliebiger Ausdruck sein kann, darf auch ein Operatorausdruck (wie unten »2 + 3.5«) als Argumentausdruck verwendet werden. Der Argumentwert ist dann der Wert des verwendeten Operatorausdrucks.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( 2 + 3.5 )); }}transcript
5.0
Bei der Auswertung solch eines Aufrufs muß zuerst der Operatorausdruck ausgewertet werden. Dieser hat in dem obigen Beispiel den Wert »5.5«. Dann wird die Inkarnation »java.lang.Math.floor( 5.5 )« hergestellt und ausgeführt; sie liefert schließlich das Ergebnis »5.0«.
Die Reihenfolge der Auswertung ist hier also genau anders herum als die Reihenfolge im Quelltext. Im Quelltext steht (beim Lesen von links nach rechts) zuerst »floor« und dann »+«. Ausgewertet wird zuerst »+« und dann »floor«.
Operatorausdrücke als Argumentausdrücke veranschaulichen noch einmal den Unterschied zwischen Argumentausdrücken und Argumentwerten.
In den beiden folgenden Programmen ist der Argumentausdruck einmal »5.5« und einmal »2 + 3.5«, der Argumtwert ist aber in beiden Fällen derselben.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( 5.5 )); }}transcript
5.0
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( 2 + 3.5 )); }}transcript
5.0
Für das Ergebnis einer Auswertung eines Methodenaufrufs kommt es nur auf den Argumentwert an, nicht auf den Argumentausdruck.
Das folgende Diagramm zeigt noch einmal die einzelnen Schritte der Auswertung von »java.lang.Math.floor( 2 + 3.5 )«.
- Schritte bei der Auswertung von »java.lang.Math.floor( 2 + 3.5 )«
2 + 3.5
1.) | Operationsvorgang
v5.5 Operationsprodukt
2.) | Inkarnationsvorgang
v (Einsetzen des Argumentwertes)java.lang.Math.floor( 5.5 ) Inkarnationsprodukt
3.) | Operationsvorgang
v5.0 Operationsprodukt
Ein Inkarnationsvorgang ist das Einsetzen von Werten für Operanden (damit sind hier auch Argumente gemeint). Beispielsweise das Einsetzen des Wertes »5.5« für den Argumentausdruck »2 + 3.5«.
Ein Inkarnationsprodukt ist das Ergebnis eines Inkarnationsvorganges, beispielsweise »java.lang.Math.floor( 5.5 )«. In einem Inkarnationsprodukt sind alle Operanden (einschließlich der Argumenten) Werte. Falls ein Methodenaufruf (wie beispielsweise »java.lang.Math.random()«) gar keine Ausdrücke enthält, so kann dieser bereits so wie er ist als Inkarnationsprodukt angesehen werden.
Falls nur von einer „Inkarnation “ gesprochen wird, so ist damit – je nach Zusammenhang – entweder der Vorgang oder das Produkt einer Inkarnation gemeint.
Ein Operationsvorgang ist die Ausführung eines Inkarnationsproduktes, beispielsweise die Auswertung von »java.lang.Math.floor( 5.5 )«.
Ein Operationsprodukt ist das Ergebnis eines Operationsvorgangs, beispielsweise der Wert »5« als Produkt der Auswertung von »java.lang.Math.floor( 5.5 )«.
Falls nur von einer „Operation “ gesprochen wird, so ist damit der Vorgang einer Operation gemeint.
Falls die Inkarnation oder Operation zu einem Operator beziehungsweise einer Methode gehört, sprechen wir von einer Operatorinkarnation oder Operatoroperation beziehungsweise einer Methodeninkarnation beziehungsweise einer Methodenoperation.
Namen als Argumentausdrücke
Das folgende Beispiel zeigt, daß auch ein Name (hier »java.lang.Math.PI«) als Argumentausdruck vorkommen kann. Der Argumentwert ist dann der Wert des verwendeten Namens.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( java.lang.Math.PI )); }}transcript
3.0
Das folgende Diagramm zeigt die einzelnen Schritte bei der Auswertung von »java.lang.Math.floor( java.lang.Math.PI )«.
- Schritte bei der Auswertung von »java.lang.Math.floor( java.lang.Math.PI )«
java.lang.Math.PI
1.) | Vorgang des Auslesen
v des Wertes des Namens3.141592653589793 Ausgelesener Wert
2.) | Inkarnationsvorgang
| (Einsetzen des
v Argumentwertes)java.lang.Math.floor( 3.141592653589793 ) Inkarnationsprodukt
3.) | Operationsvorgang
v3.0 Operationsprodukt
Aufrufausdrücke als Argumentausdrücke
Das folgende Beispiel zeigt, daß auch ein Aufrufausdruck (hier »java.lang.Math.random()«) als Argumentausdruck vorkommen kann. Der Argumentwert ist dann der Wert einer Auswertung des als Argumentausdrucks verwendeten Aufrufausdrucks.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( java.lang.Math.random() )); }}java.lang.System.out
0.0
Das voranstehende Beispiel zeigt, daß ein Argumentwert auch ein Laufzeitwert (also ein vor der Ausführung des Programms noch nicht bekannter Wert) sein darf. Durch Lesen des Quelltextes kann man den Argumentwert also gar nicht erfahren, man sieht nur den Argumentausdruck! Auch dies verdeutlicht wieder den Unterschied zwischen Argumentwert und Argumentausdruck.
Das folgende Diagramm zeigt die einzelnen Schritte bei der Auswertung von »java.lang.Math.floor( java.lang.Math.random() )«. Da der Wert einer Auswertung von »java.lang.Math.random()« von der Auswertung abhängt, können wir hier nur einen möglichen Wert als Beispiel herausgreifen.
- Schritte bei der Auswertung von »java.lang.Math.floor( java.lang.Math.random() )«
java.lang.Math.random() Inkarnationsprodukt
1.) | Operationsvorgang zur
v angegebenen Inkarnation0.8765990144933389 Operationsergebnis (Beispiel)
2.) | Inkarnationsvorgang
| (Einsetzen des
v Argumentwertes)java.lang.Math.floor( 0.8765990144933389 ) Inkarnationsprodukt
3.) | Operationsvorgang
v0.0 Operationsprodukt
Argumentausdrücke mit Aufrufen und Operatoren
Es ist auch möglich, daß ein Argumentausdruck Namen, Operatoren und Aufrufausdrücke gleichzeitig enthält.
Wir hatten bei dem folgenden Beispielprogramm schon gesehen, daß die Ausgabe immer »0.0« ist.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( java.lang.Math.random() )); }}java.lang.System.out
0.0
Da es langweilig ist, wenn die Ausgabe immer gleich ist, zeigen wir das folgende Beispielprogramm, welches nun sowohl einen Methodennamen als auch einen Operator im Argumentausdruck enthält.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( 3 * java.lang.Math.random() )); }}transcript
0.0
transcript
1.0
transcript
2.0
Das direkt obenstehende Programm kann nun bei jedem weiteren Start einen anderen Wert als beim vorherigen Start ausgeben.
Dieses Programm illustriert deutlich den Unterschied zwischen Argumentausdruck („Argument“) und Argumentwert : Der Argumentausdruck, ist immer derselbe, nämlich »3 * java.lang.Math.random()«; der Argumentwert kann bei jeder Auswertung des Aufrufs ein anderer sein.
Auch hier hängen einige Werte nun wieder von der Auswertung ab und können deswegen im folgenden nur durch willkürlich ausgewählte Beispiele dargestellt werden.
- Mögliche Schritte bei der Auswertung von »java.lang.Math.floor( 3 * java.lang.Math.random() ))«
java.lang.Math.random() Inkarnationsprodukt
1.) | Vorgang der Ermittlung
v einer Zufallszahl0.5702739229044615 Zufallszahl (zwischen 0 und 1)
2.) | Inkarnationsvorgang
| (Einsetzen der
v Operandenwerte)3 * 0.5702739229044615 Inkarnationsprodukt
3.) | Operationsvorgang
v1.7108217687133846 Operationsprodukt
(zwischen 0 und 3)4.) | Inkarnationsvorgang
| (Einsetzen des
v Argumentwertes)java.lang.Math.floor( 1.7108217687133846 ) Inkarnationsprodukt
5.) | Operationsvorgang
v1.0 Operationsprodukt
(0.0, 1.0 oder 2.0)
Das folgende Diagramm zeigt den Wertebereich des Ausdrucks »java.lang.Math.random()« mit Nummernzeichen an.
Die Werte sind darin alle gleich wahrscheinlich.
- Mit »#« markierter Wertebereich des Ausdrucks »java.lang.Math.random()«
java.lang.Math.random()
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0 3.25 3.5 3.75 4.0
| ' ' ' | ' ' ' | ' ' ' | ' ' ' |####################
Das folgende Diagramm zeigt den Wertebereich des Ausdrucks »3 * java.lang.Math.random()« mit Nummernzeichen an.
Die Werte sind darin weiterhin gleich wahrscheinlich, da die Streckung des Bereichs an der Art der Verteilung der Werte nichts ändert.
- Mit »#« markierter Wertebereich des Ausdrucks »3 * java.lang.Math.random()«
3 * java.lang.Math.random()
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0 3.25 3.5
| ' ' ' | ' ' ' | ' ' ' | ' '############################################################
Solch eine Multiplikation eines Bereichs mit einem positiven Faktor nennt man auch Skalierung. Falls der Faktor größer als »1« ist, spricht man von einer Streckung. Falls der Faktor kleiner als »1« ist, spricht man von einer Stauchung.
Das folgende Diagramm zeigt den Wertebereich des Ausdrucks »java.lang.Math.floor( 3 * java.lang.Math.random() )« mit Nummernzeichen an.
Die drei Werte sind darin weiterhin gleich wahrscheinlich, weil zu jedem Wert des folgenden Diagramms ein Bereich aus dem vorigen Diagramm gehört, dessen Breite eins beträgt. Dadurch ergibt sich der Wert »1.0« gleich vielen Fällen wie der Wert »2.0« oder der Wert »3.0«.
- Wertebereich des Ausdrucks »java.lang.Math.floor( 3 * java.lang.Math.random() ))«
java.lang.Math.floor( 3 * java.lang.Math.random() ))
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0 3.25 3.5
| ' ' ' | ' ' ' | ' ' ' | ' '# # #
Die Auswertung: Erster Schritt: Die Inkarnation
Wenn eine Methode auf ein Argument angewendet wird, dann wird eine Verbindung zwischen der Methode und dem Wert des Arguments hergestellt, die auch als Aktivierungsverbund oder als eine Inkarnation der Methode bezeichnet wird. (Auch bei Methoden ohne Argument gibt es eine entsprechende Vorbereitung zum Start der Methode, die wir hier ebenfalls als „Inkarnation“ bezeichnen.)
Im Ausdruck »java.lang.Math.negateExact( 3 )« ist die Verbindung zwischen dem Methodennamen und dem Argumentwert bereits für den Programmierer sichtbar, aber bei der Ausführung des Programms muß der Computer sie bei der Auswertung dieses Ausdrucks noch einmal neu anlegen. Dies ist die Inkarnation der Methode. Das Wort „Inkarnation “ bedeutet hier den Prozeß des Zusammenfügens zweier Informationen und auch dessen Ergebnis, also die Verbindung zwischen Methode und Argumentwert.
Die Auswertung: Zweiter Schritt: Die Ausführung
Nachdem die Inkarnation hergestellt wurde, wird sie dann als eine Operation ausgeführt. Eine Operation ist ein bestimmter Vorgang der Datenverarbeitung, der ein Ergebnis liefern kann, wie hier den Wert »-3«.
Eine Methode alleine ist im allgemeinen noch keine Operation, beispielsweise kann die Methode »java.lang.Math.negateExact« alleine (ohne Inkarnation) noch nicht ausgeführt werden, weil ihr ja noch ein Argumentwert fehlt. Erst mit den Werten aller Argumente zusammen wird aus einer bloßen Methode eine Operation.
Verwendung von »java.lang.Math.ceil« **
Sollen ganzzahlige Zufallszahlen bei »1« beginnen, so könnte man auf die Idee kommen, dafür »java.lang.Math.ceil« heranzuziehen. Diese Methode rundet ihren Argumentwert auf.
Der manchmal dafür verwendete Ausdruck »java.lang.Math.ceil( 3 * java.lang.Math.random() ))« hat jedoch nicht die Zahlen von »1« bis »3« mit gleicher Wahrscheinlichkeit als Wert, sondern vielmehr die Zahlen von »0« bis »3«, wobei »0« aber viel unwahrscheinlicher ist, da »java.lang.Math.random()« auch einmal den Wert »0.0« ergeben könnte. In diesem Fall wäre »java.lang.Math.ceil( 3 * java.lang.Math.random() ))« dann gleich »0«.
- Wertebereich des Ausdrucks »java.lang.Math.ceil( 3 * java.lang.Math.random() ))«
java.lang.Math.ceil( 3 * java.lang.Math.random() ))
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0 3.25 3.5
| ' ' ' | ' ' ' | ' ' ' | ' '? # # #
Die Verwendung von »java.lang.Math.ceil( 3 * java.lang.Math.random() ))« ist also falsch, wenn gleich wahrscheinliche Zahlen aus dem Bereich von »1« bis »3« ausgedrückt werden sollen.
Verallgemeinerungen Programmierfehler
Das eben beschriebene Problem mit der Verwendung von »java.lang.Math.ceil« ist ein Beispiel für einen typischen Programmierfehler: Beim Ausprobieren scheint alles richtig zu sein, denn der Fehler tritt nur sehr selten auf. Dennoch kann er irgendwann einmal auftreten, und dann könnte dies gerade ein „schlechter Zeitpunkt“ sein, zu dem das Auftreten des Fehlers zu erheblichen Problemen führt.
Dies bedeutet: Nur, weil ein Programm sich beim Ausprobieren wie erwartet verhält, muß es noch lange nicht korrekt sein!