Lambda-Kalkül über C# (3) Grundlagen – Funktionszusammensetzung

Lambda-Kalkül über C# (3) Grundlagen – Funktionszusammensetzung

[LINQ via C#-Reihe]

[Lambda-Kalkül über die C#-Reihe]

Neueste Version:https://weblogs.asp.net/dixin/lambda-calculus-via-c-1-fundamentals

Es ist möglicherweise nicht der beste Ort, um die Funktionszusammensetzung in der Lambda-Kalkül-Serie zu diskutieren. Die Funktionskomposition wird jedoch in späteren Artikeln häufig verwendet, daher hier eine kurze Einführung.

Funktionszusammensetzung

Funktionskomposition bedeutet, einfache Funktionen zu einer komplizierteren Funktion zu kombinieren. Die Zusammensetzung von f1 und f2 ist definiert als:f2 ∘ f1. Die Anwendung dieser neuen Funktion ist:

(f2 ∘ f1) x := f2 (f1 x)

Hier implizieren die Funktionsnamen f1 und f2 die Reihenfolge der Anwendung. f2 ∘ f1 kann auch als f2 nach f1 gelesen werden.

Auch hier ist es vollkommen normal, zwei Funktionsanwendungen miteinander zu verketten, indem die Ausgabe der ersten Funktion als Eingabe der zweiten Funktion verwendet wird:

double x = 1;
double y = Math.Sqrt(Math.Abs(x));

Das Folgende ist eine kompliziertere Funktion, kombiniert mit 2 einfachen Funktionen:

Func<double, double> absAndSqrt = x => Math.Sqrt(Math.Abs(x));

Also ist absAndSqrt eine Zusammensetzung aus Math.Abs ​​und Math.Sqrt.

Generell kann eine Funktion vom Typ Func und eine Funktion vom Typ Func zu einer neuen Funktion vom Typ Func zusammengesetzt werden:

public static partial class FuncExtensions
{
    public static Func<T1, T3> o<T1, T2, T3>
        (this Func<T2, T3> function2, Func<T1, T2> function1) => 
            arg => function2(function1(arg));
}

Leider gibt es in C# keinen Platz zum Definieren benutzerdefinierter Funktionsoperatoren, daher muss die Erweiterungsmethode verwendet werden. Diese Methode heißt o, um den Operator  ∘ nachzuahmen. Außerdem werden im Lambda-Kalkül Funktionen kuriert, also ist diese eine Erweiterungsmethode gut genug.

Eingebauter Operator in anderen Sprachen

Es ist üblich, dass andere funktionale Sprachen einen eingebauten Funktionskompositionsoperator haben. In Haskell ist ∘ nur ein Punkt (.):

(.) :: (b -> c) -> (a -> b) -> a -> c
f2 . f1 = \x -> f2 (f1 x)

Und F# hat>>:

let inline (>>) f1 f2 x = f2 (f1 x)

Dies wird als Vorwärtskomposition bezeichnet. Es gibt also auch einen Rückwärtskompositionsoperator <<:

let inline (<<) f2 f1 x = f2 (f1 x)

Eigenschaften

Die Funktionszusammensetzung hat zwei wichtige Eigenschaften

Assoziativität

Die Funktionskomposition ist assoziativ. Das heißt, (f3 ∘ f2) ∘ f1 und f3 ∘ (f2 ∘ f1) sind gleich.

Bei Anwendung von x auf (f3 ∘ f2) ∘ f1, gemäß der Definition von ∘:

  ((f3 ∘ f2) ∘ f1) (x)
≡ (f3 ∘ f2) (f1 (x))
≡ f3 (f2 (f1 (x)))

Und bei Anwendung von x auf f3 ∘ (f2 ∘ f1):

  f3 ∘ (f2 ∘ f1)
≡ f3 ∘ (f2 (f1 (x)))
≡ f3 (f2 (f1 (x)))

Sie führen also zum identischen Ergebnis. In C# bedeutet dies, dass f3.o(f2).o(f1) und f3.o(f2.o(f1)) gleich sind.

Einheit

Es gibt eine Einheitsfunktion für die Funktionszusammensetzung:

Id := λx.x

also:

f ∘ Id ≡ f

und

Id ∘ f ≡ f

In C# lautet die ID:

public static partial class FuncExtensions
{
    public static T Id<T>
        (T value) => value;
}