2017-05-24 2 views
4

Ich lerne ein wenig funktionale Programmierung und Blick auf Toolz. Die Unterschiede zwischen compose, pipe, thread_first und thread_last erscheinen mir sehr subtil oder nicht existent. Was sind die verschiedenen Anwendungsfälle für diese Funktionen?Verständnis toolz Use Cases

Antwort

4
  • compose vs. thread_* und pipe

    compose ist ein im Wesentlichen ein function compostion (∘). Es ist das Hauptziel, verschiedene Funktionen in wiederverwendbare Blöcke zu kombinieren. Reihenfolge der Anwendungen ist umgekehrt im Vergleich zu der Reihenfolge der Argumente, so compose(f, g, h)(x) ist f(g(h(x))) (das gleiche wie (f ∘ g) (x) ist f (g (x))).

    thread_* und pipe sind über wiederverwendbare Blöcke, um einen einzelnen Datenfluss zu erstellen. Die Ausführung kann nur mit verzögerten Operationen verzögert werden, Blöcke sind jedoch behoben. Reihenfolge der Anwendung ist die gleiche wie Reihenfolge der Argumente, so pipe(x, f, g, h) ist h(g(f(x))).

  • compose vs thread_*.

    compose ermöglicht keine zusätzlichen Argumente, während thread_* tut. Ohne currying compose kann nur mit unären Funktionen verwendet werden.

    Im Vergleich zu diesen thread_ kann mit Funktionen höherer arity verwendet werden, einschließlich häufig verwendeter Funktionen höherer Ordnung:

    thread_last(
        range(10), 
        (map, lambda x: x + 1), 
        (filter, lambda x: x % 2 == 0) 
    ) 
    

    Um die gleichen Sache mit compose müssen Sie currying:

    pipe(
        range(10), 
        lambda xs: map(lambda x: x + 1, xs), 
        lambda xs: filter(lambda x: x % 2 == 0, xs) 
    ) 
    

    oder

    from toolz import curried 
    
    pipe(
        range(10), 
        curried.map(lambda x: x + 1), 
        curried.filter(lambda x: x % 2 == 0) 
    ) 
    
  • thread_first vs. thread_last.

    thread_first setzt pipeed Argument an der ersten Position für die Funktion.

    thread_last setzt pipeed Argument an der letzten Position für die Funktion.

    Zum Beispiel

    >>> from operator import pow 
    >>> thread_last(3, (pow, 2)) # pow(2, 3) 
    8 
    >>> thread_first(3, (pow, 2)) # pow(3, 2) 
    9 
    

In der Praxis (einige Formalismus ignoriert wird) diese Funktionen typischerweise untereinander austauschbar sind, vor allem, wenn sie mit functools.partial/toolz.curry und einigen lambda Ausdrücke kombiniert, aber je nach Kontext, ist es einfach bequemer übereinander zu verwenden.

Zum Beispiel mit eingebauten Funktionen höherer Ordnung, wie map oder functools.reduce, thread_last ist eine natürliche Wahl. Wenn Sie ein Stück Code an mehreren Stellen wiederverwenden möchten, verwenden Sie besser compose(h, g, f) als das Hinzufügen des Funktionswrappers def fgh(x) pipe(x, f, g, h). Und so weiter.