num = 37
if num > 100:
print('größer')
else:
print('nicht größer')
print('Erledigt')nicht größer
Erledigt
Damit unsere Programme Entscheidungen treffen können, verwenden wir Mechanismen zur Kontrolle des Programmflusses. Kontrollstrukturen bestimmen, welche Teile eines Programms in welcher Reihenfolge ausgeführt werden. Python bietet drei solcher Strukturen; Bedingungen, Schleifen und Ausnahmen . Die Vermittlung des Umgangs mit Ausnahmen liegt außerhalb des Rahmens dieses Kurses. Wenn Sie jedoch mehr darüber erfahren möchten, können Sie uns gerne fragen oder sich Kapitel 4 von Effective Computation in Physics ansehen.
Bisher haben wir nur einfache Python-Anweisungen verwendet, die linear nacheinander ausgeführt wurden. Bei der Kontrollstruktur geht es genau darum, die Ausführungsreihenfolge von Codeteilen durcheinander zu bringen.
Ein Codestück, das linear zusammen ausgeführt wird, wird als Codeblock bezeichnet. Python verwendet Leerzeicheneinrückungen, um Programmblöcke abzugrenzen:
Block1
Block1
Block2
Block2
Block1
Block1
Block3
Block3
Block4
Block4
Block3
Block3
Block1
Block1Blöcke können Unterblöcke enthalten. Die empfohlene Einrückungstiefe ist 4 Leerzeichen.
Dieser Abschnitt stammt teilweise aus Software Carpentry.
Mit einer if-Anweisung können wir Python auffordern, abhängig von einer Bedingung unterschiedliche Aktionen auszuführen:
num = 37
if num > 100:
print('größer')
else:
print('nicht größer')
print('Erledigt')nicht größer
Erledigt
Die zweite Zeile dieses Codes verwendet das Schlüsselwort „if“, um Python mitzuteilen, dass wir eine Wahl treffen möchten. Wenn der Test, der auf die „if“-Anweisung folgt, wahr ist, der Körper des „Wenn“. (d. h. der darunter eingerückte Block) werden ausgeführt. Wenn der Test falsch ist, Stattdessen wird der Hauptteil von „else“ ausgeführt. Es wird immer nur das eine oder das andere ausgeführt:
Die relevanten Vergleichsoperatoren sind in der folgenden Tabelle aufgeführt. Angenommen, „a = 10“, „b = 20“.
| Betreiber | Beschreibung | Beispiel |
|---|---|---|
| == | Wenn die Werte zweier Operanden gleich sind, wird die Bedingung wahr. | (a == b) ist nicht wahr. |
| != | Wenn die Werte zweier Operanden nicht gleich sind, wird die Bedingung wahr. | |
| <> | Wenn die Werte zweier Operanden nicht gleich sind, wird die Bedingung wahr. | (a <> b) ist wahr. Dies ähnelt dem !=-Operator. |
| > | Wenn der Wert des linken Operanden größer ist als der Wert des rechten Operanden, wird die Bedingung wahr. | (a > b) ist nicht wahr. |
| < | Wenn der Wert des linken Operanden kleiner als der Wert des rechten Operanden ist, wird die Bedingung wahr. | (a < b) ist wahr. |
| >= | Wenn der Wert des linken Operanden größer oder gleich dem Wert des rechten Operanden ist, wird die Bedingung wahr. | (a >= b) ist nicht wahr. |
| <= | Wenn der Wert des linken Operanden kleiner oder gleich dem Wert des rechten Operanden ist, wird die Bedingung wahr. | (a <= b) ist wahr. |
Bedingte Anweisungen müssen nicht unbedingt ein „else“ enthalten. Wenn es keinen gibt, Python macht einfach nichts, wenn der Test falsch ist:
num = 53
print('vor bedingt...')
if num > 100:
print('53 ist größer als 100')
print('...nach bedingt')vor bedingt...
...nach bedingt
Wir können auch mehrere Tests mit elif verketten, was die Abkürzung für else if ist. Der folgende Python-Code verwendet elif, um das Vorzeichen einer Zahl auszugeben.
num = -3
if num > 0:
print(num, "is positive")
elif num == 0:
print(num, "is zero")
else:
print(num, "is negative")-3 is negative
Im obigen Code wurde ein doppeltes Gleichheitszeichen == verwendet, um Gleichheit zu testen. Im Gegensatz hierzu sind einzelne Gleichheitszeichen die Zuweisung eines Wertes zu einer Variable. Wir können Tests auch mit and und or kombinieren. and ist nur wahr, wenn beide Teile wahr sind:
if (1 > 0) and (-1 > 0):
print('Beide Teile sind wahr')
else:
print('Ein Teil ist nicht wahr')Ein Teil ist nicht wahr
während or wahr ist, wenn mindestens ein Teil wahr ist:
if (1 < 0) or (-1 < 0):
print('Mindestens ein Test ist wahr')Mindestens ein Test ist wahr
Nutzen Sie Ihr Notizbuch der letzten Woche und lösen Sie diese Aufgaben. Nutzen Sie die Markdown-Zellen, um sich Notizen zu machen.
Wie viele Wege?
Welche der folgenden Zeilen würde gedruckt werden, wenn Sie diesen Code ausführen würden? Warum haben Sie diese Antwort gewählt?
if 4 > 5:
print('A')
elif 4 == 5:
print('B')
elif 4 < 5:
print('C')Was ist Wahrheit?
Nachdem Sie den folgenden Code gelesen und ausgeführt haben, Erklären Sie die Regel, welche Werte als wahr und welche als falsch gelten.
if '':
print('empty string is true')
if 'word':
print('word is true')
if []:
print('empty list is true')
if [1, 2, 3]:
print('non-empty list is true')
if 0:
print('zero is true')
if 1:
print('one is true')Nah genug
Schreiben Sie einige Bedingungen, die True ausgeben, wenn die Variable a innerhalb von 10 % der Variablen b liegt andernfalls False. Vergleichen Sie Ihre Implementierung mit der Ihres Partners: Bekommt man für alle möglichen Zahlenpaare die gleiche Antwort?
In der Informatik ist es durchaus üblich, eine Aktion auf einen großen Datensatz anzuwenden. Wenn Sie beispielsweise das Volumen \(V\) eines Finite-Elemente-Modells berechnen möchten, müssen Sie dies tun - Rufen Sie den Jacobi-Wert der Formfunktionen \(J_i\) für jeden Gauß-Punkt und die zugehörigen Gewichte ab. - Berechnen Sie das Integral des Jacobian über das gesamte Netz mithilfe der Gaußschen Integration \[V = \sum_i J_i\, w_i\]
Angenommen, wir haben die Daten in zwei Listen (nächste Woche erfahren wir mehr über effizientere Container für Rechendaten).
J = [ 0.68317717, 0.01132594, 0.36122905, 0.94205695,
0.69374011, 0.79858585, 0.26400973, 0.64446646]
W = [ 2.0 , 1.0 , 0.88888889, 0.55555556,
2.0 , 1.0 , 0.88888889, 0.55555556]Mit dem, was Sie letztes Mal gelernt haben, scheint die Lösung dieser scheinbar einfachen Aufgabe erschreckend mühsam zu sein:
volume = J[0]*W[0] + J[1]*W[1] + J[2]*W[2] + J[3]*W[3] + J[4]*W[4] + J[5]*W[5] + J[6]*W[6] + J[7]*W[7]
print(volume)5.000916056634814
Dies ist nicht nur mühsam, sondern auch nahezu nutzlos, wenn sich die Daten ändern, beispielsweise weil Sie Ihr Netz verfeinert haben und längere Listen haben. Die Lösung für dieses Problem ist die Verwendung von Schleifen.
Der am weitesten verbreitete Schleifentyp von Python (und der einzige, den wir hier zeigen) ist die sogenannte for-Schleife, die normalerweise zum Durchlaufen eines Containers verwendet wird (genauer gesagt, über jedes iterable, aber dazu später mehr). Eine for-Schleife hat die folgende Syntax:
for <loop-var> in <iterable>:
<do action using <loop-var>>Die Aktion wird so oft ausgeführt, wie Elemente im Iterable vorhanden sind, und <loop-var> nimmt bei jeder Passage den Wert eines neuen Elements des Iterables an. Ein einfaches Beispiel:
for countdown in [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, "Happy new year!"]:
print(countdown)10
9
8
7
6
5
4
3
2
1
Happy new year!
In diesem Fall war das Iterable eine „Liste“, aber Python hat viele iterierbare Objekte, zum Beispiel Strings:
word = "Hakuna"
for letter in word:
print(letter)H
a
k
u
n
a
Wörterbücher:
my_dict = {"height": 0.21022, "width": 0.29730, "title": "The life of Edgar"}
for key in my_dict:
print(key, ":", my_dict[key])height : 0.21022
width : 0.2973
title : The life of Edgar
Zwei besonders praktische Iterablen sind range und zip:
word1 = "Hakuna"
word2 = "Matata"
print("Schleife 1:")
for i in range(len(word1)):
print(word1[i])
print("Schleife 2:")
for letter1, letter2 in zip(word1, word2):
print(letter1, letter2)Schleife 1:
H
a
k
u
n
a
Schleife 2:
H M
a a
k t
u a
n t
a a
Verwenden Sie die Notebook-Hilfe (sehen Sie sich die Sitzung der letzten Woche an, wenn Sie vergessen haben, wie man sie verwendet), um zu verstehen, was range, len und zip bewirken. Lassen Sie sich nicht einschüchtern, wenn Sie die Hilfe für zip nicht verstehen, sondern fragen Sie uns. Das Lesen der Dokumentation für eine neue Programmiersprache ist gewöhnungsbedürftig.
Lösen Sie das FEM-Netzvolumenproblem auf intelligentere Weise mit der Funktion range.
Lösen Sie das FEM-Netzvolumenproblem auf intelligentere Weise mit der zip-Funktion. Welche Version bevorzugen Sie und warum?
Loops sind großartig, aber manchmal etwas übertrieben. Stellen Sie sich vor, Sie möchten die Elemente einer Liste mit ganzen Zahlen verdoppeln oder die geraden herausfiltern:
data = [103, 117, 17, 10, 99, 14, 50, 22, 43, 119]
double_data = list()
odd_data = list()
for number in data:
double_data.append(2*number)
if number % 2 == 1:
odd_data.append(number)
double_data[206, 234, 34, 20, 198, 28, 100, 44, 86, 238]
odd_data[103, 117, 17, 99, 43, 119]
Python bietet die sogenannte list comprehension-Syntax an, die der Mengenlehre entlehnt ist.
filtered_list = [<expression> for <loop-var> in <iterable> if <condition>]Mit dieser Syntax werden die oben genannten Probleme zu Einzeilern.
data = [103, 117, 17, 10, 99, 14, 50, 22, 43, 119]
double_data = [2*number for number in data]
odd_data = [number for number in data if number%2 == 1]
double_data[206, 234, 34, 20, 198, 28, 100, 44, 86, 238]
odd_data[103, 117, 17, 99, 43, 119]
Sie haben gelernt, wie Sie Python mit if-elif-else-Anweisungen dazu bringen, Entscheidungen zu treffen, und wie Sie einfache Codefragmente mithilfe der for-Schleife wiederverwenden. Sie haben außerdem gelernt, einfache Schleifen mithilfe von Listenverständnissen prägnanter und lesbarer zu gestalten.
Was Sie bisher über Kontrollstruktur gelernt haben, sollte für diesem Kurs ausreichen. Allerdings hat Python noch viel mehr zu bieten, und es wird Ihnen dringend empfohlen, selbst mehr darüber zu lernen, insbesondere über Generatoren, Wörterbücher und Mengen, beispielsweise in Kapitel 4 von Effective Computation in Physics.
Im vorherigen Abschnitt haben wir gelernt, wie man kleine Codefragmente wiederverwendet und sie mithilfe von Schleifen und Verständnis auf beliebig große Datensätze anwendet und Python Entscheidungen auf der Grundlage dessen treffen lässt, was es in unseren Daten sieht.
Aber was ist, wenn unser Code ziemlich lang und kompliziert wird? Was wäre, wenn wir Tausende von Datensätzen hätten und ähnliche, aber nicht genau dieselben Operationen an ihnen durchführen wollten? Und was wäre, wenn wir diesen Code erneut verwenden wollten, für einen anderen Datensatz oder an einer anderen Stelle in unserem Programm? Durch das Ausschneiden und Einfügen wird unser Code sehr schnell sehr lang und sehr repetitiv. Stellen Sie sich außerdem vor, Sie finden einen Fehler in einem Codeabschnitt, den Sie an zwei Dutzend Stellen in Ihrem Programm eingefügt haben. Sie müssten es einzeln reparieren. Wir möchten unseren Code so verpacken, dass er leichter wiederverwendet werden kann. Python ermöglicht dies, indem es uns die Möglichkeit gibt, Dinge namens „Funktionen“ zu definieren – eine Abkürzung für die erneute Ausführung längerer Codeteile.
Beginnen wir mit der Definition einer Funktion fahr_to_kelvin, die Temperaturen von Fahrenheit in Kelvin umwandelt:
def fahr_to_kelvin(temp):
return ((temp - 32) * (5/9)) + 273.15Die Funktionsdefinition beginnt mit dem Wort def, gefolgt vom Namen der Funktion und einer Liste von Parameternamen in Klammern. Der Hauptteil der Funktion – die Anweisungen, die bei der Ausführung ausgeführt werden – wird unterhalb der Definitionslinie eingerückt, normalerweise um vier Leerzeichen. Wenn wir die Funktion aufrufen, werden die Werte, die wir ihr übergeben, diesen Variablen zugewiesen, sodass wir sie innerhalb der Funktion verwenden können. Innerhalb der Funktion verwenden wir eine return-Anweisung, um ein Ergebnis an denjenigen zurückzusenden, der danach gefragt hat.
Versuchen wir, unsere Funktion auszuführen. Der Aufruf unserer eigenen Funktion unterscheidet sich nicht vom Aufruf einer anderen Funktion:
print('Gefrierpunkt von Wasser:', fahr_to_kelvin(32), 'K')
print('Siedepunkt von Wasser:', fahr_to_kelvin(212), 'K')Gefrierpunkt von Wasser: 273.15 K
Siedepunkt von Wasser: 373.15 K
Wir haben die von uns definierte Funktion erfolgreich aufgerufen und haben Zugriff auf den zurückgegebenen Wert.
Nachdem wir nun gesehen haben, wie man Fahrenheit in Kelvin umwandelt, ist es ganz einfach, Kelvin in Celsius umzuwandeln:
def kelvin_to_celsius(temp):
return temp - 273.15
print('absoluter Nullpunkt in Celsius:', kelvin_to_celsius(0.0))absoluter Nullpunkt in Celsius: -273.15
Wie wäre es mit der Umrechnung von Fahrenheit in Celsius? Wir könnten die Formel aufschreiben, müssen es aber nicht. Stattdessen können wir die beiden Funktionen zusammenstellen, die wir bereits erstellt haben:
def fahr_to_celsius(temp):
return kelvin_to_celsius(fahr_to_kelvin(temp))
print('Gefrierpunkt von Wasser in Celsius:', fahr_to_celsius(32.0))Gefrierpunkt von Wasser in Celsius: 0.0
Dies ist unser erster Vorgeschmack darauf, wie größere Programme aufgebaut sind: Wir definieren grundlegende Operationen und kombinieren sie dann in immer größeren Blöcken, um den gewünschten Effekt zu erzielen.
Schauen wir uns die Syntax einer Funktion in Python genauer an: > Python > def <fun_name>(<argument(s)>): > <body> >
<fun_name>.Ist nur ein weiterer Variablenname und es gelten die gleichen Einschränkungen: Er kann alphanumerische Zeichen in Groß- und Kleinschreibung sowie Unterstriche enthalten, darf jedoch nicht mit einer Ziffer beginnen. Beachten Sie, dass Funktionen nicht denselben Namen wie eine andere Variable haben dürfen, da sie auch nur Variablen sind:
f = 24
print(f)
def f(x):
return 12
print(f) # Die Funktion hat den Variablennamen verfälscht24
<function f at 0x112d14400>
<Argument(s)>.Python ist äußerst flexibel, wenn es darum geht, wie Funktionsargumente gehandhabt werden, und wir empfehlen Ihnen, sich über die Details zu informieren (Kapitel 5 in Effective Computation in Physics). Hier betrachten wir nur Funktionen mit einer bekannten Anzahl von Argumenten, von denen einige optional sein können. Betrachten Sie als Beispiel die Logarithmusfunktion
def my_log(x, base):
from numpy import log
return log(x)/log(base)
print("log_10(100) =", my_log(100, 10))
print("log_2(1024) =", my_log(1024, 2))log_10(100) = 2.0
log_2(1024) = 10.0
It takes two arguments; der Wert, dessen Logarithmus wir berechnen möchten, und die Logarithmusbasis. Bei physikalischen Berechnungen wird jedoch nur sehr selten etwas anderes als der natürliche Logarithmus verwendet. Legen wir also \(e\) als Standardwert für base fest
from numpy import e
def my_log(x, base=e):
from numpy import log
return log(x)/log(base)
print("log_10(100) =", my_log(100, 10))
print("log_2(1024) =", my_log(1024, 2))
print("ln(2.718281828459045) =", my_log(2.718281828459045))log_10(100) = 2.0
log_2(1024) = 10.0
ln(2.718281828459045) = 1.0
Dank der Möglichkeit, Standardwerte zu definieren, kann sich my_log bei Bedarf wie zwei verschiedene Funktionen verhalten.
Funktionen können auch mehrere Argumente mit Standardwerten haben, z. B. diese Funktion zur Auswertung von Polynomen zweiter Ordnung:
def poly2(x, a=1, b=1, c=1):
return a*x**2 + b*x + cSolche optionalen Argumente werden Schlüsselwortargumente genannt, da sie in einem Funktionsaufruf (durch ihr Schlüsselwort) benannt werden können:
x0 = 12
poly2(x0)157
poly2(x0, b=3.5)187.0
<body>.Im Funktionskörper wird das Verhalten der Funktion bestimmt und die Rückgabewerte bestimmt. Es kann null oder mehrere Return-Anweisungen enthalten. Funktionen ohne return-Anweisung in ihrem Rumpf geben immer die spezielle Variable „None“ zurück
return_value = print("Was ist der Rückgabewert der Druckfunktion?")
print(return_value)Was ist der Rückgabewert der Druckfunktion?
None
Wenn in einem Funktionskörper eine return-Anweisung erreicht wird, wird die Ausführung der Funktion sofort beendet
from numpy import sin
def sin_inv_x(x):
if x == 0:
return 0.
print(("Oh no, I won't get printed because "
"I follow a return statement"))
print("Aber wenn x != 0 ist, stehe ich vor der Return-Anweisung")
return sin(1/x)
print("sin_inv_x(0.) =", sin_inv_x(0.))sin_inv_x(0.) = 0.0
print("sin_inv_x(12.) =", sin_inv_x(12.))Aber wenn x != 0 ist, stehe ich vor der Return-Anweisung
sin_inv_x(12.) = 0.08323691620031025
Für eine Funktion ist es häufig nützlich, mehrere Ergebniswerte zurückzugeben. Betrachten Sie beispielsweise eine Funktion, die die kinetische und potentielle Energie eines Pendels berechnet.
from numpy import cos, pi
def pendulum(l, m, theta, omega, g=9.8):
""" computes the mechanical energies for a pendulum of length l and
mass m at angular position theta (from the lower vertical) and
angular velocity omega.
returns:
e_kin, e_pot
"""
e_kin = 0.5* m * (l*omega)**2
e_pot = m * g * (1-cos(theta))*l
return e_kin, e_pot
l = .5
m = 12
theta = pi/2
omega = -4
energies = pendulum(l, m, theta, omega)
energies(24.0, np.float64(58.8))
Wie Sie sehen können, hat die Funktion etwas zurückgegeben, das wie eine Liste aussieht, jedoch mit Klammern (()) anstelle von Klammern ([]). Dieser Container wird Tupel genannt und auf seinen Inhalt kann durch Indizieren (wie bei Listen) oder durch Entpacken zugegriffen werden:
e_kin, e_pot = energies
print("entpackt: E_kin =", e_kin, "E_pot =", e_pot)entpackt: E_kin = 24.0 E_pot = 58.8
Wenn Sie aufgepasst haben, ist Ihnen vielleicht aufgefallen, dass sich eine Menge erklärender Text in die Funktionsdefinition eingeschlichen hat. Dies wird als Dokumentzeichenfolge bezeichnet. Nutzen Sie die Notebook-Hilfe zur Pendelfunktion. Was siehst du?
Der Variablenbereich ist in jeder Programmiersprache ein sehr wichtiges Konzept und sein Verständnis ist für die korrekte Verwendung von Funktionen von entscheidender Bedeutung. Das Grundkonzept besteht darin, dass die Lebensdauer von Variablen, die innerhalb einer Funktion definiert sind, mit dem Ende der Funktion endet. Betrachten Sie das folgende (zugegebenermaßen alberne) Beispiel:
def scopefun(x):
fun_var = 10*x
print("Ich bin im Bereich und kann fun_var sehen:", 1)
return x
print(scopefun(42))
print("Ich bin außerhalb des Gültigkeitsbereichs, Python beschwert sich:", fun_var)Ich bin im Bereich und kann fun_var sehen: 1
42
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[75], line 7 4 return x 6 print(scopefun(42)) ----> 7 print("Ich bin außerhalb des Gültigkeitsbereichs, Python beschwert sich:", fun_var) NameError: name 'fun_var' is not defined
Wie Sie sehen, sind Variablen, die in einem unteren (eingerückten) Block definiert wurden, im äußeren Block nicht sichtbar. Dies wird in Bezug auf die Funktion als lokaler Bereich bezeichnet. Wenn Sie an einem fundierteren Verständnis des Scopings interessiert sind, insbesondere am Konzept des nicht-lokalen Scopings, empfehlen wir Ihnen dringend, sich im Buch ausführlicher damit zu befassen oder es mit uns zu diskutieren.
Da Funktionen über einen eigenen Variablenbereich verfügen, können sie sich auch selbst aufrufen, ohne dass sich die Variablen in ihren Körpern gegenseitig überlagern. Normalerweise führt dies zu einer offensichtlichen und nicht überraschenden Katastrophe:
def my_dumb_recursion(): my_dumb_recursion() return my_dumb_recursion() --------------------------------------------------------------------------- RecursionError Traceback (most recent call last) Cell In[2], line 1 ----> 1 my_dumb_recursion() Cell In[1], line 2, in my_dumb_recursion() 1 def my_dumb_recursion(): ----> 2 my_dumb_recursion() 3 return Cell In[1], line 2, in my_dumb_recursion() 1 def my_dumb_recursion(): ----> 2 my_dumb_recursion() 3 return [... skipping similar frames: my_dumb_recursion at line 2 (2984 times)] Cell In[1], line 2, in my_dumb_recursion() 1 def my_dumb_recursion(): ----> 2 my_dumb_recursion() 3 return RecursionError: maximum recursion depth exceeded
Mit einer geeigneten Beendigungsklausel kann die Rekursion jedoch eine elegante Lösung sein. Betrachten Sie zum Beispiel die Berechnung der Fakultät:
def factorial(n):
if n == 1:
return 1
return n * factorial(n-1)
factorial(5)120
Die Fakultät ist ein guter Zeitpunkt, um zu erwähnen, was passiert, wenn Sie extrem große numerische Werte berechnen:
factorial(171) # Ja, Python kann große Ganzzahlen verarbeiten1241018070217667823424840524103103992616605577501693185388951803611996075221691752992751978120487585576464959501670387052809889858690710767331242032218484364310473577889968548278290754541561964852153468318044293239598173696899657235903947616152278558180061176365108428800000000000000000000000000000000000000000
factorial(171.) # Bei Fließkommazahlen gibt es einen Überlaufinf
Schreiben Sie eine rekursive Funktion fibonacci_recursive(n), die den \(n\)-ten Wert von Fibonacci sequence berechnet (wir gehen von der modernen Verwendung aus, beginnend bei Null, und beginnen bei Null zu zählen).
Schreiben Sie eine Funktion fibonacci_loop(n), die das gleiche Ergebnis wie fibonacci_recursive(n) liefert, aber eine Schleife anstelle einer Rekursion verwendet
Nachdem Sie nun die Grundlagen der Kontrollstruktur und Schleifen verstanden haben, sind Sie bereit, sich wiederholende Aufgaben bei großen Datenmengen zu automatisieren. Mit Ihrem Wissen über Funktionen sind Sie in der Lage, komplexe Probleme in einfache (und daher weniger fehleranfällige) Teilprobleme aufzuteilen, die Sie zusammenfügen können.
Dies war die letzte Übungseinheit zum Thema Python im Allgemeinen. Wenn Sie also Fragen haben, zögern Sie nicht, uns zu kontaktieren, denn nächste Woche werden wir uns mit effizienten wissenschaftlichen Berechnungen befassen.