lambda und Funktionen

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.
Stex
User
Beiträge: 14
Registriert: Donnerstag 23. April 2009, 16:15

Hallo,

ich schaue mir gerade "Text Processing in Python" von David Mertz an. Der definiert in seinem ersten Kapitel mehrere Hilfsfunktionen mittels Lambda (http://gnosis.cx/TPiP/chap1.txt).

Code: Alles auswählen

#------------------- combinatorial.py -------------------#
from operator import mul, add, truth
apply_each = lambda fns, args=[]: map(apply, fns, [args]*len(fns))
bools = lambda lst: map(truth, lst)
bool_each = lambda fns, args=[]: bools(apply_each(fns, args))
conjoin = lambda fns, args=[]: reduce(mul, bool_each(fns, args))
all = lambda fns: lambda arg, fns=fns: conjoin(fns, (arg,))
both = lambda f,g: all((f,g))
all3 = lambda f,g,h: all((f,g,h))
and_ = lambda f,g: lambda x, f=f, g=g: f(x) and g(x)
disjoin = lambda fns, args=[]: reduce(add, bool_each(fns, args))
some = lambda fns: lambda arg, fns=fns: disjoin(fns, (arg,))
either = lambda f,g: some((f,g))
anyof3 = lambda f,g,h: some((f,g,h))
compose = lambda f,g: lambda x, f=f, g=g: f(g(x))
compose3 = lambda f,g,h: lambda x, f=f, g=g, h=h: f(g(h(x)))
ident = lambda x: x
Diese Funktionen hätte ich jetzt gern als "normale" Funktionen ohne lambda definiert, allerdings steige ich gerade bei "all" aus... ;-) (lambda im lambda...) Kann mir da bitte jemand weiterhelfen? Was ich bis jetzt habe:

Code: Alles auswählen

import operator

def apply_each(functions, args=[]):
    """Ruft jede Funktion mit args auf."""
    return [f(*args) for f in functions]

def bools(sequence):
    """Prueft sequence auf True/False"""
    return map(operator.truth, sequence)

def bool_each(functions, args=[]):
    """Prueft jedes Ergebnis von function(args) auf True/False"""
    return bools(apply_each(functions, args))

def conjoin(functions, args):
    """Prueft, ob alle Ergebnisse aus function(args) True sind"""
    return reduce(operator.mul, bool_each(functions, args))

addiere = lambda x, y: x + y
multipliziere = lambda x, y: x * y

print apply_each([addiere, multipliziere], [3, 4])
print bools([False, True, True])
print bool_each([addiere, multipliziere], [3,4])
print conjoin([addiere, multipliziere], [3,0])
Stex
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Warum willst du denn die Lambdas loswerden?

Ungetestet:

Code: Alles auswählen

def all(fns):
    def _wrapper(arg):
        conjoin(fns, (arg,))
    return _wrapper
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Das doppelte lambda-konstrukt nennt sich currying oder Schönfinkeln, siehe http://de.wikipedia.org/wiki/Schönfinkeln

Beispiel:

Code: Alles auswählen

>>> t = lambda a: lambda b, a=a: b+a
>>> t(5)
<function <lambda> at 0xb7b2102c>
>>> t(5)(6)
11
>>> t(5)(7)
12
Stex
User
Beiträge: 14
Registriert: Donnerstag 23. April 2009, 16:15

Zum "Lambda-Loswerden": Zum einen wollte ich die Funktionen durcharbeiten und genau verstehen, was dabei passiert; zum anderen habe ich mal gelesen, dass Guido bereut, dass er die Lambdas eingebaut hat und ich dachte mir ich versuch es mal ohne ;-)

Danke für die Hinweise!

Stex
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Leonidas hat geschrieben:Warum willst du denn die Lambdas loswerden?
Lambdas zu bauen und dann gleich an einen Namen zu binden finde ich auch nicht so toll. Einzeiler kann man auch mit einer normalen Funktionsdefinition haben...
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

@jerch: Dank des Umlauts musst du die URL von Hand eingeben: Schönfinkeln.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Wie anonym ist eine an einen Namen gebundene lambda-Funktion eigentlich noch oder besser welchen Sinn hats? :shock:

Code: Alles auswählen

>>> t = lambda x:x
>>> t.func_name
'<lambda>'
>>> (lambda x:x).func_name
'<lambda>'
Naja immerhin...

@mkesper:
Bei mir will das nicht funktionieren mit den Umlauten :(
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

jerch hat geschrieben:Wie anonym ist eine an einen Namen gebundene lambda-Funktion eigentlich noch oder besser welchen Sinn hats?
Anonym ist die Funktion dann natürlich nicht mehr. Man spart lediglich ein paar Zeichen ein und sieht sofort, dass es sich um einen Ausruck handelt. Ich würde in so einem Fall eher zur "normalen" Funktionsdefinition tendieren.
Das Leben ist wie ein Tennisball.
Stex
User
Beiträge: 14
Registriert: Donnerstag 23. April 2009, 16:15

Sehe ich das richtig, dass "all" eigentlich nur eine andere Art des Aufrufes als "conjoin" darstellt? Also statt "conjoin(bla, bli)" "all(bla)(bli)"?
--
Ok, vorschnell geschrieben; ich glaub langsam kapier ich es. "all" liefert mir eine Funktion zurück, welche die Funktionen in conjoin kombiniert. Bei "conjoin" muss ich die Argumente gleich mitreinwerfen, was ich bei "all" nicht machen muss...
--
Aber nochmal zu "all":
bei

Code: Alles auswählen

def all2(functions):
    def _wrapper(arg):
        conjoin(functions, (arg,))
    return _wrapper
bekomme ich was anderes (nämlich immer None) raus als bei

Code: Alles auswählen

all = lambda functions: lambda arg, functions=functions: conjoin(functions, (arg,))
Das kapier ich noch nicht ;-)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

lambda gibt das Ergebnis des Ausdrucks zurück. Deine _wrapper Funktion gibt aber nichts bzw. implizit None zurück.
Stex
User
Beiträge: 14
Registriert: Donnerstag 23. April 2009, 16:15

Danke, DasIch. Das war's.

Code: Alles auswählen

def all_v2(functions):
    def _wrapper(arg):
        return conjoin(functions, (arg,))
    return _wrapper
Ich glaub ich mach mal Pause ;-)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Rebecca hat geschrieben:
Leonidas hat geschrieben:Warum willst du denn die Lambdas loswerden?
Lambdas zu bauen und dann gleich an einen Namen zu binden finde ich auch nicht so toll.
Ooh, also prinzipiell finde ich dass eigentlich alle Funktionen standardmäßig namenlos sein sollten, so wie (lambda) in Scheme oder fun/function in Ocaml. So eine ähnliche Tendenz sieht man auch oft in JavaScript.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Ich finde Sprachen sympatisch, in denen folgendes möglich ist:

Code: Alles auswählen

a = function(abc)
   Block Inhalt
end
Wenn Funktionen schon Objekte sind, sollte man sie auch wie Objekte einer Variable zuweisen können. Anonyme Blöcke (für getrennte Namensräume) finde ich auch sehr praktisch. Schade, das Python nichts vergleichbares hat.
Bottle: Micro Web Framework + Development Blog
BlackJack

Also ich finde es ganz schön wenn in einem Traceback vernünftige Namen stehen, statt '<lambda>'. Dann hat man auch ohne in den Quelltext zu schauen, schon eine Idee davon was ungefähr betroffen sein könnte.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Defnull hat geschrieben:Wenn Funktionen schon Objekte sind, sollte man sie auch wie Objekte einer Variable zuweisen können.
Kann man ja auch. Bloß existiert kein Literal für anonyme Funktionen. Wäre syntaktisch auch schwierig.

Code: Alles auswählen

some_function(lambda x: x**7, "Hallo Welt")
#vs.
some_function(block x:
    result = x
    for i in range(6): result *= x
    return result, "Hallo Welt" #<-- Wie will man jetzt das 2. Funktionsargument  ermöglichen
)
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Eines stimmt: Die Idee, anonyme Funktionen direkt als Parameter an andere Funktionen zu übergeben ist mit dem Python Whitespace-Block-Syntax fast unmöglich sauber hin zu bekommen.

Was ich mit "Wenn Funktionen schon Objekte sind, sollte man sie auch wie Objekte einer Variable zuweisen können" meinte, war aber eher folgendes:

Code: Alles auswählen

d = dict()
d['func1'] = def():
   pass

# und

o = object()
o.func1 = def():
   pass

# statt

def anon():
   pass
d['func1'] = anon
o.func1 = anon
Das wäre durchaus machbar und würde das "Funktionen sind auch nur Objekte" Prinzip konsequenter umsetzen finde ich. Es spart einfach den Umweg über den lokalen Namensraum.
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

mkesper hat geschrieben:@jerch: Dank des Umlauts musst du die URL von Hand eingeben: Schönfinkeln.

Code: Alles auswählen

[url=http://de.wikipedia.org/wiki/Sch%C3%B6nfinkeln]Schönfinkeln[/url]
Ich mache das immer so: Wort(e) markieren, URL-Knopf drücken und dann in das

Code: Alles auswählen

[url=]
die URL nach dem = reinkopieren.
Stex
User
Beiträge: 14
Registriert: Donnerstag 23. April 2009, 16:15

-- delete --
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Stex hat geschrieben:-- delete --
Den Thread?! Da kannst du lange warten. Das wird hier nicht passieren ;)
Oder hast du nur einen Beitrag von dir verunglimpft?
Stex
User
Beiträge: 14
Registriert: Donnerstag 23. April 2009, 16:15

Ja, hab einen Beitrag zurückgenommen, nachdem ich auf die Lösung durch nachdenken doch noch selbst gekommen bin ;) Thread darf schon bleiben (Frage war ursprünglich, wie ich "both" umsetze)

Code: Alles auswählen


import operator

#------------------------------------------------------------------------------


# numerische Testfunktionen
sum_ = lambda x, y: x + y
product = lambda x, y: x * y
delta = lambda x, y: x - y

# boolsche Testfunktionen
isOdd = lambda x: x % 2 != 0
isEven = lambda x: x % 2 == 0
isLess10 = lambda x: x < 10
isGreater10 = lambda x: x > 10
isMultiple5 = lambda x: x % 5 == 0


#------------------------------------------------------------------------------


def apply_each(functions, args=[]):
    """Ruft jede Funktion mit args auf. Gibt die Ergebnisse als Liste zurueck.
    --> list

    >>> apply_each([sum_, product, delta], [3, 4])
    [7, 12, -1]
    """
    return [f(*args) for f in functions]


def bools(sequence):
    """Prueft sequence auf True/False
    --> list

    >>> bools([3, 4, 0, 5])
    [True, True, False, True]
    >>> bools([[], [1, 2]])
    [False, True]
    """    
    return map(operator.truth, sequence)


def bool_each(functions, args=[]):
    """Prueft jedes Ergebnis von function(*args) auf True/False
    --> list

    >>> bool_each([isOdd, isEven], [3])
    [True, False]
    >>> bool_each([sum_, product], [4, 0])
    [True, False]
    """    
    return bools(apply_each(functions, args))


def conjoin(functions, args):
    """Gibt 1 zurueck, falls alle functions mit args True ergeben, sonst 0.
    --> int

    >>> conjoin([isEven, isGreater10], [14])
    1
    >>> conjoin([isOdd, isGreater10], [14])
    0
    >>> conjoin([isOdd, isGreater10, isMultiple5], [15])
    1
    """
    return reduce(operator.mul, bool_each(functions, args))


def all(functions):
    """Gibt eine Funktion zurueck, die alle (boolean-)functions
    kombiniert (boolsche und-Verknuepfung)
    --> function

    >>> all((isOdd, isGreater10))(13)
    1
    >>> all((isOdd, isGreater10))(14)
    0
    >>> all((isOdd, isGreater10, isMultiple5))(15)
    1
    """
    def _wrapper(args):
        return conjoin(functions, (args,))
    return _wrapper


def both(f, g):
    """Gibt eine Funktion zurueck, die f und g kombiniert (boolsches and).
    --> function

    >>> both(isOdd, isGreater10)(14)
    0
    >>> both(isOdd, isGreater10)(15)
    1
    """
    return all((f, g))


def all3(f, g, h):
    """Gibt eine Funktion zurueck, die f, g und h kombiniert (boolsches and).
    --> function

    >>> all3(isOdd, isGreater10, isMultiple5)(14)
    0
    >>> all3(isOdd, isGreater10, isMultiple5)(15)
    1
    >>> all3(isOdd, isGreater10, isMultiple5)(17)
    0
    """
    return all((f, g, h))


#------------------------------------------------------------------------------


def _test_module():
    import doctest
    doctest.testmod(verbose=True)

if __name__ == "__main__":
    _test_module()
    #print both(isOdd, isGreater10)(14)
Antworten