Seite 1 von 1

Guter Weg um "Skalar" an eine Liste zu addieren

Verfasst: Dienstag 17. März 2009, 01:03
von Hyperion
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!

Verfasst: Dienstag 17. März 2009, 01:14
von Leonidas
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.

Verfasst: Dienstag 17. März 2009, 01:21
von Hyperion
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!

Verfasst: Dienstag 17. März 2009, 01:31
von BlackVivi

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.

Verfasst: Dienstag 17. März 2009, 01:49
von Hyperion
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 ;-)

Verfasst: Dienstag 17. März 2009, 02:02
von DasIch
@BlackVivi partial gibt es genau dafür, dann sollte man kein lambda verwenden. Vorallem weil lambda recht beschrenkt im Gegensatz zu partial.

Verfasst: Dienstag 17. März 2009, 09:05
von CM
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:

Verfasst: Dienstag 17. März 2009, 09:23
von Zap
Wenn ich das richtig verstanden habe verhält sich map() ab Python3.x wie vorher imap().
Oder übersehe ich da jetzt was?!

Verfasst: Dienstag 17. März 2009, 09:53
von CM
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

Verfasst: Dienstag 17. März 2009, 10:53
von Leonidas
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 :)

Verfasst: Dienstag 17. März 2009, 14:05
von DasIch
@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.

Verfasst: Mittwoch 25. März 2009, 01:39
von str1442

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.

Verfasst: Mittwoch 25. März 2009, 09:23
von Leonidas
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.

Verfasst: Mittwoch 25. März 2009, 14:14
von str1442
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.

Verfasst: Mittwoch 25. März 2009, 16:02
von Leonidas
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.

Verfasst: Donnerstag 26. März 2009, 16:50
von Leonidas
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.