Guter Weg um "Skalar" an eine Liste zu addieren

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
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo,

ich habe heute mal ein wenig mit dem itertools- und operator-Modulen rumgespielt und habe da auch einiges realisieren können, aber für folgendes Problem fällt mir keine gute Lösung ein.

Ich möchte gerne eine Art Skalar mit einer Operation auf eine Liste anwenden, in etwa so:

Code: Alles auswählen

In [1]: import operator as op

In [2]: l = [1, 2, 3]

In [3]: skalar = 6

In [5]: from itertools import imap

In [6]: list(imap(op.add, [skalar]*3, l))
Out[6]: [7, 8, 9]
Das ist irgend wie nicht wirklich "hübsch", da ich die "3" kennen muss und außerdem eine neue Liste erzeugt wird.

Wäre für nen Tipp dankbar!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Was du suchst sind partielle Funktionen, auch bekannt als Currying oder auf deutsch Schönfinkeln:

Code: Alles auswählen

from functools import partial
from operator import add
from itertools import imap

l = [1, 2, 3]
print list(imap(partial(add, 6), l))
Damit erstellst du quasi eine unäre Funktion bei dem ein Argument schon festgelegt wird. Du kannst dir auch die Folien vom 20. Usertreffen in München ansehen, dort gibt es zwei Beispiele dazu.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich danke Dir! Dieses "partial" habe ich bisher nicht verstanden - aber ich werde mir die Folien mal angucken; beim "Unicode"-Ärger haben sie mir damals (14. Treffen?) auch sehr geholfen!

Ich glaube wenn man die itertools- und functools-Module verstanden hat, kann man viele Dinge wesentlich eleganter ausdrücken in Python!
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Code: Alles auswählen

>>> import operator as op
>>> l = [1, 2, 3]
>>> skalar = 6
>>> from itertools import imap
>>> list(imap(lambda x: op.add(x, skalar), l))
[7, 8, 9]
Was spricht dagegen o_o? Wahrscheinlcih denk ich zu ... meh, doof.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackVivi hat geschrieben: Was spricht dagegen o_o? Wahrscheinlcih denk ich zu ... meh, doof.
Ich denke das ist genau eines der lambdas, von denen Leonidas in seinen Folien spricht ;-)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

@BlackVivi partial gibt es genau dafür, dann sollte man kein lambda verwenden. Vorallem weil lambda recht beschrenkt im Gegensatz zu partial.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Gerade erst an imap gewöhnt und jetzt schon py3 ... Wie sähe eigentlich eine 3.x-Lösung ohne imap aus?

Gruß,
Christian

PS Erwähnte ich schon numpy? *duck* :wink:
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Wenn ich das richtig verstanden habe verhält sich map() ab Python3.x wie vorher imap().
Oder übersehe ich da jetzt was?!
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Sieht in der Tat so aus:

Code: Alles auswählen

help(map)
in 3.0 ergibt:

Code: Alles auswählen

Help on class map in module builtins:

class map(object)
 |  map(func, *iterables) --> map object
 |  
 |  Make an iterator that computes the function using arguments from
 |  each of the iterables.  Stops when the shortest iterable is exhausted.
:oops:
Danke,
Christian
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Man muss sich einfach vorstellen dass die ``i``-Varianten der ``itertools`` zum Standard werden. Daher habe ich im Beispiel oben auch ``imap`` und ``list`` verwendet, wo auch ein normales ``map`` gereicht hätte.

Zum Thema ``partial``: das ist generell ein recht interessantes Thema. In Python kann es verwendet werden um positionelle Argumente zu setzen, mit dem linken startend. Das entspricht wie ich erst vor paar Tagen festgestellt habe, ``curry`` in Scheme. Weiterhin nimmt Pythons ``partial`` auch Keyword-Argumente (siehe auch das Beispiel in meinen Folien, da wird das verwendet), so dass man auch die Möglichkeit hat die Funktions-Parameter von rechts aufzufüllen (in Scheme macht man das mit ``curryr``, was aber eigentlich nicht so flexibel ist). Wozu man das braucht? Nun, ich wollte aus einer aufsteigenden Zahlenreihe (xrange) Zyklen von 0 bis 4 machen, also jedes Element Modulo 5 rechnen. Mit ``partial(mod, 5)`` bekommt man aber ``5 % zahl``. Somit kann man sich hier mit ``partial(mod, b=5)`` abhelfen, wo dann ``zahl % 5`` rauskommt.

Ein weitere nette Anwendung von Currying sind funktionale Sprachen die nur Funktionen mit einem Argument haben. Ich meine dass da Haskell dazugehört, denn dort ist ein ``add = lambda a, b: a+b`` ein ``add = lambda b: lambda a: a+b``. Dies bedeutet, dass wenn man dieser Funktion keine zwei Parameter sondern nur einen mitgibt, man automatisch so eine partielle Funktion bekommt: ``add(5)`` ist dann ``lambda a: a+5`` und wenn man ``add(5)(1)`` ausführt dann bekommt man regulär 6 als Ergebnis. Natürlich gibt es dafür syntaktischen Zucker, man muss seine Funktionen nicht selbst verschachteln :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

@Leonidas Stimmt Haskell gehört dazu.

Code: Alles auswählen

Prelude> let add a b = a + b
Prelude> let foo = add 1
Prelude> foo 2
3
Prelude> :type add
add :: (Num a) => a -> a -> a
Prelude> :type foo
foo :: Integer -> Integer
Man sieht dass am Typ der Funktion.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Code: Alles auswählen

In [6]: op.add(3, b=5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/media/Daten/Projects/Qadesh/<ipython console> in <module>()

TypeError: add() takes no keyword arguments
Hat das irgendeinen guten Grund? (Python 2.5.4)

Das hier:
Mit ``partial(mod, 5)`` bekommt man aber ``5 % zahl``. Somit kann man sich hier mit ``partial(mod, b=5)`` abhelfen, wo dann ``zahl % 5`` rauskommt.
musste ich eigentlich mit op.add selbst anwenden und funktioniert auch nicht, gleiche Fehlermeldung (mit add statt mod). Das heißt, partial() an sich funktioniert natürlich schon, kracht dann aber beim Aufruf. Hmpf, behelf ich mir mit lambda.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Lass halt die Keyword-Argumente weg, Addition ist ja kommutativ also muss du nicht einen spezielles Argument belegen, es reicht ja nur dass eines von beiden belegt wird.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Integer Addition: Ja, Tupel Addition: Nein. Auf die Idee wär ich sonst auch gekommen ;P Obwohl, ich hätte vielleicht nicht grade Zahlen zum Testen nehmen sollen und das ohne weiteren Kommentar hier posten sollen. Dennoch kann das wohl nur eine Beschränkung des operator Modules sein, welches in C geschrieben ist (bzw zumindest wird dann eine operator.so geladen). Nur die Frage, warum.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

DasIch hat geschrieben:Stimmt Haskell gehört dazu.
Gut das bestätigt bekommen. In Scheme kann man das über Makros nachrüsten :)

@str1442: Tatsache, soweit hab ich nicht gedacht :oops:. Ich kann dir auch nicht sagen warum das so ist; ich würde sagen, dass das so nicht sein sollte. Vielleicht kann man ja ein Ticket deswegen aufmachen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Leonidas hat geschrieben:@str1442: Tatsache, soweit hab ich nicht gedacht :oops:. Ich kann dir auch nicht sagen warum das so ist; ich würde sagen, dass das so nicht sein sollte. Vielleicht kann man ja ein Ticket deswegen aufmachen.
Nachdem birkenfeld mir auch keinen guten Grund nennen könnte warum das so ist, habe ich ein Ticket dazu aufgemacht. Erstaunlich dass das bisher noch niemand gemacht hat.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten