Eine der Aufgaben aus dem letzten Kapitel lautete, einen Weg zu
finden, der sicherstellt, dass bei einer Division zweier Variablen
ein Ergebnis mit Kommastellen herauskommt, und zwar unabhängig
davon, ob die Variablen Realzahlen oder Ganzzahlen enthalten. Die
Lösung könnte z.B. so aussehen:
(/(* 1.0 zahl1)zahl2).
Damit wäre eines der Argumente auf jeden Fall eine Realzahl, und
die Korrektheit des Ergebnisses wäre sichergestellt. AutoLISP
bietet jedoch in Gestalt der Funktion
(float) eine einfachere
Lösung:
(/(float zahl1)zahl2)
;oder auch
(/ zahl1(float zahl2))
(float) wandelt eine Integerzahl in eine Realzahl um (wenn
das Argument schon eine Realzahl ist, passiert gar nichts). Bedenken
Sie aber, dass der Inhalt der Variablen hier in keiner Weise geändert
wird, nur die Rückgabe der Evaluation wird modifiziert! Den Variablenwert
selbst müssen Sie mit (setq) ändern.
Noch ein Beispiel, um das zu verdeutlichen:
(setq vari_1 7) => 7
(setq vari_2 2) => 2
(/ vari_1 vari_2) => 3
;Ganzzahldivision!
(float vari_1) => 7.0
;Gibt zwar den geänderten Rückgabewert zurück,
;ändert aber nicht den Wert der Variablen vari_1!
(/ vari_1 vari_2) => 3
;immer noch Ganzzahldivision!
(/(float vari_1)vari_2) => 3.5
;Realzahldivision, die Variablen haben aber
;immer noch den alten Wert
(setq vari_1(float vari_1)
;Erst jetzt ändert sich der Wert der Variablen
Auch
(setq) kann in verschachtelten Ausdrücken
vorkommen:
(setq z3(+(setq z1 -3)(setq z2(1- z1))))
Hier bekommt
z3 den Wert -7 zugewiesen. Überlegen
Sie nun, warum die folgende Eingabe (wahrscheinlich) nicht
korrekt ist:
(setq radius1
(*
(setq radius3(/(float radius2)2))
(setq radius2 25)
)
)
Ist der Groschen bei Ihnen gefallen? Es wird der Variablen
radius3 der halbe Wert von
radius2 zugewiesen, bevor
radius2 überhaupt einen Wert hat. Es entsteht also ein
Problem der Reihenfolge der Abarbeitung. Die Einschränkung, dass
die Eingabe 'wahrscheinlich' nicht korrekt ist, hat folgenden
Grund: Es könnte ja sein, dass
radius2 vorher schon
irgendwoher einen alten Wert hatte, der jetzt überschrieben wird.
Mit anderen Worten: Wenn Sie jetzt diese Zeile eintippen, werden
Sie sich mit Sicherheit die gelbe Karte einhandeln. Sieht man die
Zeile als Bruchstück eines Programms, könnte Sie durchaus korrekt
sein.
Es gibt übrigens eine Gegenteilsfunktion zu
(float), die
(fix) heisst und die (nein, es wird nichts schneller) Real-
in Integerzahlen umwandelt. Das bedeutet aber, dass der nichtganzzahlige
Anteil verlorengeht. Ausprobieren!
Und nun zu einem Thema, das Ihnen am Anfang vielleicht etwas Schwierig-
keiten verursachen wird: Wenn LISP einen Ausdruck evaluiert, wird in der
Regel jedes Element des Ausdrucks (von links nach rechts) evaluiert.
Wenn Sie also den Ausdruck
(+ var1 var2) eingeben, passiert
folgendes:
-
Der Funktionsname + wird evaluiert. Dies bedeutet, dass statt
des Namens der Funktion die Funktion selbst eingesetzt wird
-
Das Symbol var1 wird evaluiert, nehmen wir an, der Wert sei 6.
-
Das Symbol var2 wird evaluiert, nehmen wir an, der Wert sei 4.
-
Jetzt kann der Funktionsablauf (in diesem Fall der
Additionsprozess), der an den Namen + gebunden ist, mit
den Argumenten 6 und 4 durchgeführt werden.
Untersuchen wir nun eine Zuweisung wie z.B.:
(setq durchmesser 3.5).
Analog zum vorigen Beipiel müsste also folgendes passieren:
-
Der Funktionsname setq wird evaluiert und die Funktion selbst
eingesetzt.
-
Das Symbol durchm wird evaluiert und durch seinen Wert ersetzt.
???
Halt! Das kann ja wohl nicht sein! Wir wollen dem Symbol
durchmesserdoch erst einen Wert zuweisen!
Es ist tatsächlich so, dass Argumente einer Funktion nicht
ausnahmslos evaluiert werden. Wann Argumente evaluiert werden
und wann nicht, hängt von der aufgerufenen Funktion ab. Bei
(setq ...) ist es so, dass das erste Argument nicht
evaluiert wird, jedoch das zweite. Bei den Rechenfunktionen
werden grundsätzlich alle Argumente evaluiert. Übrigens: wenn
Zahlen evaluiert werden, ist der Wert der Zahl immer die Zahl
selbst.
Also nochmal: Der Evaluations-Ablauf von
(setq durchmesser 3.5)
-
Der Funktionsname setq wird evaluiert und die Funktion
selbst eingesetzt.
-
Das Symbol durchmesser wird nicht evaluiert, sondern
bleibt als Name stehen
-
Die Zahl 3.5 wird zwar evaluiert. Die Evaluation von
Zahlen ergibt aber immer die Zahl selbst, also ändert sich
auch hier nichts.
-
Jetzt kann der an den Namen setq gebundene Ablauf mit den
Argumenten durchmesser und 3.5 durchgeführt werden, d.h.
die Zahl 3.5 wird an das Symbol durchmesser gebunden.
Es gibt ausser setq noch weitere Funktionen, die verhindern, dass ihre
Argumente ganz oder teilweise evaluiert werden. Doch dazu später.
Nun zu einer weiteren Funktion namens
(set), die fast das
selbe tut wie
(setq), aber mit dem kleinen Unterschied,
dass sie die Evaluation ihres ersten Arguments nicht verhindert. Und
dann gibt es noch die Funktion
(quote). Diese bekommt ein
Argument und verhindert, dass das Argument evaluiert wird. Sie kann
jederzeit und überall dazu eingesetzt werden, um eine Evaluation
zu verhindern.
Auch wenn jetzt vielleicht ein deutliches Stöhnen zu vernehmen ist,
sehen Sie sich doch bitte einmal dieses Beispiel an:
(set(quote durchm)3.5)
Was passiert hier? Erstens: Wie gehabt wird der Funktionsname
set durch die Funktionsprozedur selbst ersetzt. Zweitens:
Im Gegensatz zu
(setq) verhindert
(set) nicht die
Evaluation des ersten Arguments. Durch den Einsatz von
(quote)
wird sie nun aber doch verhindert. Drittens: Die Zahl 3.5 wird zu
3.5 evaluiert, weil Zahlen immer sich selbst ergeben, wenn sie
evaluiert werden. Wir können also schliessen, dass die beiden
Ausdrücke
(setq durchm 3.5) und
(set(quote durchm)3.5)
ein völlig identisches Verhalten an den Tag legen. Das heisst aber
nicht, dass sie identisch sind - sie sind äquivalent.
Was können wir nun mit
(set) und
(quote) anfangen?
Versuchen Sie, das nächste Beispiel nachzuvollziehen:
(setq var1(quote var2))
(set var1 99)
Ganz kurz gefasst: Wir weisen der Variablen
var1 den Wert
var2 zu. Anschliessend weisen wir dem Wert von
var1
den Wert 99 zu. Es hat jetzt also, da
var1 den Wert
var2 hat, die Variable
var2 den Wert 99.
Die Welt ist voller Was-wäre-wenn, also lassen Sie uns noch
ein paar solcher Überlegungen anstellen. Was wäre also, wenn
wir zuerst
(set var1(quote var2)) geschrieben hätten?
Antwort: var1 wäre evaluiert worden. Da es noch keinen Wert hatte,
hätte die Sache nicht funktioniert. Was wäre gewesen, wenn wir
var2 nicht gequotet hätten? Wir hätten damit
var1
den Wert 'keinen Wert' zugewiesen. Keinen Wert hatte aber
var1 sowieso schon. Und was wäre gewesen, wenn wir
(setq var1 99) als zweite Anweisung eingegeben hätten?
Dann hätten wir nicht
var2, sondern
var1 den Wert
99 zugewiesen.
Noch einige Dinge, die nicht unerwähnt bleiben dürfen:
(setq ...) ist einfach eine Abkürzung für 'set quoted'.
Für die Funktion (quote) gibt es eine andere Schreibweise: Man
setzt einfach ein Hochkomma vor den betreffenden Ausdruck. Das
letzte Beispiel sieht dann so aus:
(setq var1 'var2) => var2
(set var1 99) => 99
Der Wert "Kein-Wert" heisst in LISP
nil. Die Evaluation
eines Ausdrucks ohne Wert ergibt immer
nil. Wenn wir also
annehmen, dass den Variablen
var1 und
var2 bisher
kein Wert zugeordnet wurde, ergibt sich dieses Bild:
(setq var1 var2) => nil
;Der Variablen var1 wird der Wert nil zugewiesen,
;da var2 keinen Wert hat und die Evaluation von
;var2 nil ergibt. Es ändert sich also gar nichts,
;da var1 auch vor dieser Operation den Wert nil
;hatte.
(setq var1 77) => 77
;Jetzt hat var1 den Wert 77
(setq var1 nil) => nil
;Alles wieder wie vorher - kein Wert.
Die Funktion
(setq) kann auch mehr als zwei Argumente
haben. Sie können eingeben:
(setq a 1 b 2 c 3) und
erzielen damit das selbe Ergebnis wie durch Eingabe von
(setq a 1)(setq b 2)(setq c 3). Die Argumente müssen
aber immer paarweise vorhanden sein. Selbstverständlich wird
die Evaluation jedes ersten Argumentes eines Paares verhindert.
Und hier mal wieder das Wichtigste aus den beiden letzten Kapiteln
in Kürze zusammengefasst:
-
Zuweisungen von Werten an Namen (Symbole) führt man mit der Funktion
(setq) durch.
-
Man sollte darauf achten, daá man durch die Wahl von Symbolnamen
keine bestehenden AutoLISP-Funktionen entdefiniert.
-
Variablennamen sollten deskriptiv sein, d.h. man sollte auch am
übernächsten Tag noch erkennen können, was man sich dabei
gedacht hat.
-
Wenn ein Ausdruck evaluiert wird, wird (von links nach rechts)
jedes seiner Elemente evaluiert, es sei denn, die Evaluation wird
verhindert.
-
Zahlen werden durch Evaluation nicht verändert.
-
Es gibt Funktionen, die die Evaluation eines oder mehrerer ihrer
Argumente verhindern.
-
Die Funktion (setq) verhindert die Evaluation ihres
ersten Argumentes.
-
Die Funktion (set) verhindert keine Evaluation ihrer
Argumente.
-
Die Funktion (quote) dient dazu, die Evaluation eines
beliebigen Ausdrucks zu verhindern.
-
'Kein-Wert' heisst in AutoLISP nil. Das Zuweisen von
nil mit (setq) bedeutet das Entfernen eines
evtl. zugewiesenen Wertes.
Übungsaufgaben
-
Sie haben folgendes eingegeben:
(setq var1 27) => 27
(float var1) => 27.0
Wenn Sie nun folgendes eingeben: (setq var2 var1),
welchen Wert hat dann die Variable var2?
-
Sie haben folgendes eingegeben:
(setq var1 49)
(setq var2(*(sqrt var1)(sqrt var1)))
Hat var2 jetzt den selben Wert wie var1?
-
Sie haben folgendes eingegeben:
(setq var1 103 var2 -37 var3(+ var1 var2) var1 0)
Welchen Wert hat jetzt var3?
-
Sie haben folgendes eingegeben:
(setq var3(+(setq var1 103)(setq var2 -37))var1 0)
Ist jetzt alles genauso wie in der vorigen Aufgabe, und
ist dies überhaupt eine korrekte Eingabe?
-
Kann man auch (set) genauso wie (setq) mit
beliebig vielen Paaren von Argumenten aufrufen?
-
Wieviele Argumente kann man der Funktion (quote) mitgeben?
-
Zählen Sie alle Funktionen auf, die bisher bekannt sind, mit
denen man den Wert einer Variablen verändern kann.
-
Welche Aussagen sind richtig:
-
Die Funktion (quote) sorgt dafür, dass ein Funktionsargument
evaluiert wird, auch wenn die Funktion die Evaluation
eigentlich verhindert.
-
Der Ausdruck (quote x) kann auch (' x)
geschrieben werden.
-
Die Ausdrücke (set(quote y)3) und (setq y 3)
sind völlig identisch.
-
Die Ausdrücke (setq(quote y)3) und (set y 3)
sind in der Wirkung gleich
-
Den Wert nil kann man gar nicht zuweisen.
-
Symbole, die keinen Wert haben, gibt es nicht.
-
Welche der folgenden Eingaben erzeugt einen Fehler?
(setq nil 3)
(setq 3 nil)
(set 'a 'b)
(setq a 1 b 2 c 3)
(set 'a 1 'b 2 'c 3)
(setq var1(float 1.5))
(setq var1(fix pi))
-
Sie sind nur am Nachkommaanteil des Wertes der Variablen
real_var interessiert. Wie berechnen Sie ihn?
-
Ist es richtig, dass die Funktion (setq) in
AutoLISP gar nicht unbedingt notwendig ist, d.h.
wäre AutoLISP irgendwie eingeschränkt, wenn es
(setq) nicht gäbe?
-
Welchen Wert hat die Variable a zum Schluss, wenn
Sie folgendes nacheinander eingeben:
(setq a 'b)
(setq b 'c)
(set a 1)
(set b 2)