Seite 1 von 2

lambda und Funktionen

Verfasst: Dienstag 1. Dezember 2009, 21:07
von Stex
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

Verfasst: Dienstag 1. Dezember 2009, 21:23
von Leonidas
Warum willst du denn die Lambdas loswerden?

Ungetestet:

Code: Alles auswählen

def all(fns):
    def _wrapper(arg):
        conjoin(fns, (arg,))
    return _wrapper

Verfasst: Dienstag 1. Dezember 2009, 21:24
von jerch
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

Verfasst: Dienstag 1. Dezember 2009, 21:28
von Stex
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

Verfasst: Mittwoch 2. Dezember 2009, 09:12
von Rebecca
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...

Verfasst: Mittwoch 2. Dezember 2009, 11:00
von mkesper
@jerch: Dank des Umlauts musst du die URL von Hand eingeben: Schönfinkeln.

Verfasst: Mittwoch 2. Dezember 2009, 13:52
von jerch
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 :(

Verfasst: Mittwoch 2. Dezember 2009, 14:01
von EyDu
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.

Verfasst: Mittwoch 2. Dezember 2009, 20:23
von Stex
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 ;-)

Verfasst: Mittwoch 2. Dezember 2009, 21:22
von DasIch
lambda gibt das Ergebnis des Ausdrucks zurück. Deine _wrapper Funktion gibt aber nichts bzw. implizit None zurück.

Verfasst: Mittwoch 2. Dezember 2009, 21:31
von Stex
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 ;-)

Verfasst: Mittwoch 2. Dezember 2009, 22:45
von Leonidas
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.

Verfasst: Donnerstag 3. Dezember 2009, 00:34
von Defnull
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.

Verfasst: Donnerstag 3. Dezember 2009, 09:17
von 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.

Verfasst: Donnerstag 3. Dezember 2009, 11:44
von Darii
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
)

Verfasst: Donnerstag 3. Dezember 2009, 13:04
von Defnull
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.

Verfasst: Donnerstag 3. Dezember 2009, 13:59
von mkesper
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.

Verfasst: Donnerstag 3. Dezember 2009, 15:51
von Stex
-- delete --

Verfasst: Donnerstag 3. Dezember 2009, 15:54
von Zap
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?

Verfasst: Donnerstag 3. Dezember 2009, 15:59
von Stex
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)