@eastlib: Die Einrückung sollte vier Leerzeichen pro Ebene betragen.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das heisst das Hauptprogramm das in dem ``if __name__ …``-Zweig steht, sollte in einer Funktion verschwinden, damit es keine globalen Variablen gibt.
Das würde dann auch gleich zwei Probleme aufdecken: `c1` und `c2` werden von einer Funktion benutzt, ohne dass die das als Argumente übergeben bekommt. Funktionen und Methoden sollten aber alles was sie ausser Konstanten benötigen als Argumente übergeben bekommen.
Und `fit_func()` versucht ein `xVals` zu verwenden was es gar nicht gibt.
`numpy` wird importiert, sogar unter zwei Namen, dann aber überhaupt nicht verwendet‽ Entweder sollte man das verwenden, dann machen parallel geführte Arrays für `R` und V` Sinn, oder man löst das ohne Numpy, *dann* sollte man aber mindestens mal `R` und `V` zu *einer* Liste aus Partikelobjekten zusammenführen die jeweils eine Position und eine Geschwindigkeit haben. Da wären wir übrigens wieder bei schlechten Namen: Das `V` wohl für „velocity“ stehen soll habe ich jetzt mal geraten. Wofür `R` steht – keine Ahnung. Wenn man da nicht so dusselige Einbuchstabennamen verwenden würde, müsste man nicht raten und würde viel besser verstehen was der Code eigentlich machen soll. Ähnliches für `nPop` was man auch `particle_count` nennen könnte, dann braucht man da nicht rätseln.
Insgesamt sieht es so aus als würdest Du nicht in Python sondern in einer anderen Programmiesprache schreiben wollen. Zum Beispiel dieses ``for i in range(0, n):`` ist kein Python sondern eher C oder Pascal, oder so. Zum einen ist das `n` ja immer die Länge einer der übergebenen Listen, also ist das als Argument an die jeweilige Funktion völlig überflüssig, denn Listen kennen in Python ihre eigene Länge. Das muss man also nicht übergeben, sondern kann es einfach mit `len()` abfragen. Und dann haben wir ``for i in range(0, len(sequence)):`` und in der Schleife wird dann `i` verwendet um auf die Sequenz und ”paralelle” Sequenzen zuzugreifen. Das ist in Python ein „anti pattern“ weil man *direkt* über die Elemente von Sequenzen iterieren kann. Wenn man über mehrere parallel iterieren möchte, gibt es die `zip()`-Funktion. Und wenn man *zusätzlich* eine laufende Zahl in der Schleife benötigt die `enumerate()`-Funktion.
Du würdest dann wahrscheinlich versucht sein die `enumerate()`-Funktion zu verwenden weil Du Listen elementweise verändert – aber das ist auch sehr unüblich in Python. Da erstellt man einfach eine *neue* Liste und gibt die dann zurück.
Um das mal an der `update_position()` zu demonstrieren:
Code: Alles auswählen
def update_position(R, V, nPop, nVar, xMin, xMax):
for p in range(0, nPop):
for i in range(0, nVar):
R[p][i] = R[p][i] + V[p][i]
if R[p][i] > xMax: R[p][i] = xMax
if R[p][i] < xMin: R[p][i] = xMin
Erst einmal `nPop` eleminieren:
Code: Alles auswählen
def update_position(R, V, nVar, xMin, xMax):
for rx, vx in zip(R, V):
for i in range(0, nVar):
rx[i] = max(min(rx[i] + vx[i], xMax), xMin)
Hier fallen wieder die Namen unangenehm auf, weil ich keine wirkliche Ahnung habe was `R` und `V` genau bedeuten. Also werden auch die Folgenamen doof.
Im nächsten Schritt jetzt `nVar` beseitigen und nicht die Eingabedaten verändern, sondern eine neue Datenstruktur zurück geben:
Code: Alles auswählen
def update_position(R, V, xMin, xMax):
result = list()
for rx, vx in zip(R, V):
new_rx = list()
for r, v in zip(rx, vx):
new_rx.append(max(min(r + v, xMax), xMin))
result.append(new_rx)
return result
Als abschluss sieht man hier dann noch das sich „list comprehensions“ geradezu aufdrängen. Erst die innere Schleife:
Code: Alles auswählen
def update_position(R, V, xMin, xMax):
result = list()
for rx, vx in zip(R, V):
result.append([max(min(r + v, xMax), xMin) for r, v in zip(rx, vx)])
return result
Und dann die Äussere:
Code: Alles auswählen
def update_position(R, V, xMin, xMax):
return [
[max(min(r + v, xMax), xMin) for r, v in zip(rx, vx)]
for rx, vx in zip(R, V)
]
*Das* sieht jetzt schon mehr nach Python aus. Allerdings sind die `zip()`\s ja nur notwendig weil die zusammengehörenden Werte in der verschiedenen Datenstrukturen stehen. Das macht nur Sinn wenn es Numpy-Arrays wären, denn dann würde die Funktion nur noch so aussehen:
Code: Alles auswählen
def update_position(R, V, xMin, xMax):
return (R + V).clip(xMin, xMax)
Wenn man Numpy verwendet schreibt man nur noch ganz wenige Schleifen in Python. Wenn man per Schleife über Nunpy-Arrays iteriert, macht man in 99% der Fälle etwas falsch.
Sollte es bei Code ohne Numpy bleiben, dann würde man Partikelobjekte einführen die jeweils die X-Position und die Geschwindigkeit kennen, und der Code würde so aussehen:
Code: Alles auswählen
def update_position(particles, min_x, max_x):
for row in particles:
for particle in row:
particle.x = max(min(particle.x + particle.velocity, max_x), min_x)
Abschliessend: Den gezeigten Code kannst Du so nicht laufen lassen haben, denn neben dem Einrückproblem bei ``if __name__ …`` versuchst Du auf `fitFunc` per Index zuzugreifen. Das geht bei Funktionsobjekten nicht. Was sollte das auch bedeuten? Und `fitFunc()` verwendet `xVals` was nirgends definiert ist, die Funktion würde so also auch nicht funktionieren, wenn man sie aufrufen würde.