Lambda Funktion

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
th_goebi
User
Beiträge: 3
Registriert: Freitag 3. Dezember 2021, 16:47

Hallo Python-Forum Freunde,

ich habe für eine Routine (in COBYLA) folgende lambda-Funktionen definiert:

con[1] = lambda x: x[1] - x[0]--1

con[2] = lambda x : x[2] - x[0] -1

con[3] = lambda x : x[3] -x[0] -1

usw. bis:

con[n] = lambda x : x[n] - x[0] -1


Mein Programm funktioniert damit.

Kann ich die n Funktionen auch mithilfe einer "for i in range (1,n): ..." Schleife vereinfacht programmieren? Ich bekomme das nicht hin.
Danke für eure Hilfe.
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@th_goebi: Das Problem das Du haben dürftest ist das Namen erst aufgelöst werden wenn der Code der Ausdruck ausgewertet wird. Also das `i` bei dem ``x[ i ]`` hat dann bei allen Funktionen den letzten Wert den `i` in der ``for``-Schleife hatte. Oder gar einen anderen wenn `i` danach noch einmal durch weiteren Code verändert wurde.

Es gibt zwei übliche Arten das zu lösen: Werte für Defaultargumente werden nur einmal ausgewertet, nämlich zum Zeitpunkt der Definition von Funktionen. Und `functools.partial()`.

Ungetestet:

Code: Alles auswählen

    for i in range(1, n + 1):
        con[i] = lambda xs, i=i: xs[i] - xs[0] - 1

    # oder

    for i in range(1, n + 1):
        con[i] = partial((lambda i, xs: xs[i] - xs[0] - 1), i)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
narpfel
User
Beiträge: 644
Registriert: Freitag 20. Oktober 2017, 16:10

Da die Funktionen sich alle in genau einem Wert unterscheiden, würde ich da nur eine Funktion schreiben und in `con` jeweils nur das `i` speichern:

Code: Alles auswählen

def f(x, i):
    return x[i] - x[0] - 1

con = range(1, n + 1)
...
do_something_with(f(x, con[value]))
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@narpfel: Ich habe mir COBYLA nicht näher angeschaut, aber bei solchen „constraints solving/optimization“-Bibliotheken muss man die Constraints oft in Form von Funktionen angeben. Der Solver entscheidet dann wann er welche Funktion aufruft.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
th_goebi
User
Beiträge: 3
Registriert: Freitag 3. Dezember 2021, 16:47

Vielen Dank für eure Antworten. Es funktioniert leider nicht, da er in con und x jeweils "i" reinschreibt und nicht den aktuellen Wert (1,2, ... ,n) der Schleifenvariablen. Die COBYLA-Routine braucht aber die con's so, wie ich es in meinem Post angegeben habe, sonst stürzt das Programm ab. Gibt es eventuell noch einen anderen Weg?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was genau hast du jetzt versucht? Der Ansatz von __blackjack__ sollte eigentlich aequivalent sein.

Code: Alles auswählen

con = []
con.append(lambda x: x[1] - x[0] - 1)
con.append(lambda x: x[2] - x[0] - 1)
con.append(lambda x: x[3] - x[0] - 1)

bound_con = []
for i in range(1, 4):
    bound_con.append(lambda x, i=i: x[i] - x[0] - 1)

x = list(range(100))

for manual, generated in zip(con, bound_con):
    print(manual(x), generated(x))
Wenn er das nicht ist, dann muss da noch etwas anderes vor sich gehen. Hast du Fehlermeldungen?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: falls das Paket irgendwie durch Introspektion versucht, etwas ueber die gegebenen Funktionen rauszufinden, dann koennte das hier helfen:

Code: Alles auswählen

con = []
con.append(lambda x: x[1] - x[0] - 1)
con.append(lambda x: x[2] - x[0] - 1)
con.append(lambda x: x[3] - x[0] - 1)

bound_con = []
for i in range(1, 4):
    def get(x, i=i):
        return x[i]
    bound_con.append(lambda x: get(x) - x[0] - 1)

x = list(range(100))

for manual, generated in zip(con, bound_con):
    print(manual(x), generated(x))
Die generierten Funktionen behalten nach aussen hin die gleiche Signatur.
th_goebi
User
Beiträge: 3
Registriert: Freitag 3. Dezember 2021, 16:47

Nachtrag: nach weiterer Suche im Netz und etwas Testen habe ich eine Lösung für mein Problem gefunden. Ich erstelle die Lambda-Funktionen zunächst als Strings und wandele sie dann mit "eval()" in die benötigten Funktionen um.
.....
str_lambda=[" "] * (n+1)
for i in range(1,n+1):
str_lambda = "lambda x : x[" + str(i) + "] - x[0] + 1"
funct_lambda = eval(str_lambda)
con = funct_lambda
.....
Damit funktioniert mein Code jetzt. Nochmals vielen Dank für eure Beiträge, die mir geholfen haben, diese Lösung zu finden.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@th_goebi: Das ist das brunzdümmste, was ich je gelesen habe. Gratulation!
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@th_goebi: `eval()` ist so gut wie nie eine sinnvolle Lösung.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@th_goebi: Ich habe gerade durch Dich gelernt, dass man den Empfang privater Nachrichten deaktivieren kann. Das hast Du nämlich anscheinend, weshalb ich Dir nicht auf Deine private Nachricht antworten kann. 🤓 Jedenfalls nicht privat.

Deshalb öffentlich: `eval()` nicht verwenden weil das Böse™ ist („eval() is evil“). Das kann beliebige Python-Ausdrücke ausführen. Wenn dort Daten eingesetzt werden, die von aussen kommen, ist das im Zweifelsfall sogar gefährlich. Und letztlich kann da auch jeder Fehler auftreten, inklusive Syntaxfehler zur Laufzeit. Es ist also schwer da eine robuste und sinnvolle Fehlerbehandlung zu machen. Es ist auch nur sehr selten sinnvoll, denn wenn man zum generieren von Quelltext greift, muss das für ein Problem sein, dass man in der Sprache selbst nicht lösen kann. Das kann man aber. Du hast zwei Wege gezeigt bekommen die einfach sind, funktionieren, und genau für diesen Zweck gedacht sind, und ”innerhalb der Sprache” bleiben. Also ausnutzen, dass Defaultargumente zum Zeitpunkt der Funktionsdefinition ausgewertet werden, oder `functools.partial()`, das eingeführt wurde weil der Weg über Defaultargumente ein bisschen ”magisch” erscheinen mag, und man auch mal Fälle hat, wo man schon vorhandene, benannte Funktionen als Grundlage verwenden möchte.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten