Seite 1 von 1

Putting the fun back in functional

Verfasst: Montag 3. August 2009, 15:26
von tordmor
Ich hab mal ein Modul angefangen um ein paar Ideen von Haskell nach Python zu bringen. Ich bin mir aber noch nicht sicher, ob sie da sinnvoll sind.

Code: Alles auswählen

class F(functools.partial):
    """Adds function composition to functools.partial.
    E.g. F(map, f) * F(filter, g) ->
    lambda iter: map(f, filter(g, iter))"""

    def __mul__(left, right):
        return F(lambda *args, **kwargs: left(right(*args, **kwargs)))

def flip(f):
    "Takes a function that takes two arguments and returns a function "
    "with the arguments reversed."
    return lambda x, y: f(y, x)

def maybe(value, just_f, none_f):
    "returns just_f(value) if value is not None else none_f()."
    return none_f() if value is None else just_f(value)

def maybe_args(just_f, none_f, *args, **kwargs):
    "returns just_f with all additional arguments if none of them are None "
    "else none_f()"
    return none_f() if (
            any(arg is None for arg in args) or
            any(arg is None for arg in kwargs.values())
        ) else just_f(*args, **kwargs)
Mit der F erweiterung kann man * als Function composition operator verwenden:

Code: Alles auswählen

get_session_id = F(string.rstrip, chars='L') * F(string.lstrip, chars='0x') * F(hex) * F(random.getrandbits, 128)

Verfasst: Montag 3. August 2009, 18:05
von Leonidas
Jetzt will ich noch die Currying-Semantik von Haskell haben :D Ich habs mal in Scheme nachgebaut, in Python würde man wohl eine Klasse basteln, die __call__ so überschreibt, dass sie entweder die Funktion aufruft oder eine neue Instanz seiner selbst zurück.

Vielleicht implementiere ich das ja mal beizeiten...

Verfasst: Montag 3. August 2009, 19:10
von EyDu
Beim Currying gibt es bei Python nur ein sehr ärgerliches Problem. Was macht man (sinnvoll) mit Funktionen, bei denen Default-Werte gegeben sind? Natürlich könnte man, wenn man denn __call__ implementiert, einen Aufruf ohne Parameter tätigen, das sieht aber irgendwie dumm aus. Außer "nicht zulassen" oder Sonderbehandlung einführen fällt mir nichts vernünftiges ein.

Verfasst: Montag 3. August 2009, 19:23
von sma
Du meinst so was?

Code: Alles auswählen

def curryable(f, n=None):
    if n is None:
        n = len(f.func_code.co_varnames)
    if n > 1:
        return lambda x: curryable(lambda *args: f(x, *args), n - 1)
    return f
        
@curryable
def add(a, b, c):
    return a + b + c
    

print add(3)(4)(6)
Stefan