Funktionales Denken. Teil 8

Hallo Habr! Wir kehren etwas spät von den Neujahrsferien zurück und setzen unsere Artikelserie über funktionale Programmierung fort. Heute werden wir über das Verstehen von Funktionen durch Signaturen und das Definieren Ihrer eigenen Typen für Funktionssignaturen sprechen. Details unter dem Schnitt!




Nicht offensichtlich, aber F # hat zwei Syntaxen: für reguläre (aussagekräftige) Ausdrücke und für Typdefinitionen. Zum Beispiel:


[1;2;3] //   int list //   Some 1 //   int option //   (1,"a") //   int * string //   

Ausdrücke für Typen haben eine spezielle Syntax, die sich von der Syntax gewöhnlicher Ausdrücke unterscheidet. Möglicherweise haben Sie bei der Arbeit mit FSI (FSharp Interactive) viele Beispiele für diese Syntax bemerkt Die Typen der einzelnen Ausdrücke werden zusammen mit den Ergebnissen ihrer Ausführung angezeigt.


Wie Sie wissen, verwendet F # einen Typinferenzalgorithmus, sodass Sie häufig keine expliziten Typen in Code schreiben müssen, insbesondere in Funktionen. Um jedoch effektiv mit F # arbeiten zu können, müssen Sie die Syntax von Typen verstehen, damit Sie Ihre eigenen Typen definieren, Typkonvertierungsfehler debuggen und Funktionssignaturen lesen können. In diesem Artikel werde ich mich auf die Verwendung von Typen in Funktionssignaturen konzentrieren.


Hier einige Beispiele für Typensyntaxsignaturen:


 //   //   let add1 x = x + 1 // int -> int let add xy = x + y // int -> int -> int let print x = printf "%A" x // 'a -> unit System.Console.ReadLine // unit -> string List.sum // 'a list -> 'a List.filter // ('a -> bool) -> 'a list -> 'a list List.map // ('a -> 'b) -> 'a list -> 'b list 

Funktionen durch Signaturen verstehen


Selbst wenn Sie nur die Signatur einer Funktion studieren, können Sie sich oft ein Bild davon machen, was sie tut. Betrachten Sie einige Beispiele und analysieren Sie sie nacheinander.


 int -> int -> int 

Diese Funktion verwendet zwei int Parameter und gibt einen weiteren int . Höchstwahrscheinlich handelt es sich um eine Art mathematischer Funktionen wie Addition, Subtraktion, Multiplikation oder Exponentiation.


 int -> unit 

Diese Funktion nimmt ein int und gibt eine unit , was bedeutet, dass die Funktion als Nebeneffekt etwas Wichtiges tut. Weil Es wird kein nützlicher Wert zurückgegeben. Der Nebeneffekt führt höchstwahrscheinlich zu Schreibvorgängen in E / A, z. B. Protokollierung, Schreiben in die Datenbank oder ähnliches.


 unit -> string 

Diese Funktion akzeptiert nichts, gibt jedoch eine string , was bedeuten kann, dass die Funktion eine Zeichenfolge aus der Luft empfängt. Da es keine explizite Eingabe gibt, macht die Funktion wahrscheinlich etwas mit Lesen (etwa aus einer Datei) oder Generieren (wie einer zufälligen Zeichenfolge).


 int -> (unit -> string) 

Diese Funktion nimmt ein int und gibt eine andere Funktion zurück, die beim Aufruf einen String zurückgibt. Wahrscheinlich führt die Funktion wieder eine Lese- oder Generierungsoperation aus. Die Eingabe wird wahrscheinlich die Rückgabefunktion irgendwie initialisieren. Beispielsweise kann die Eingabe die Kennung einer Datei sein, und die Rückgabefunktion ähnelt readline() . Oder die Eingabe kann der Startwert für einen Zufallszeichenfolgengenerator sein. Wir können nicht sicher sagen, aber wir können einige Schlussfolgerungen ziehen.


 'a list -> 'a 

Die Funktion akzeptiert eine Liste eines beliebigen Typs, gibt jedoch nur einen Wert dieses Typs zurück. Dies kann darauf hinweisen, dass die Funktion die Liste aggregiert oder eines ihrer Elemente auswählt. Eine ähnliche Signatur ist List.sum , List.max , List.head usw.


 ('a -> bool) -> 'a list -> 'a list 

Diese Funktion akzeptiert zwei Parameter: Der erste ist eine Funktion, die etwas in einen bool (Prädikat) konvertiert, der zweite ist eine Liste. Der Rückgabewert ist eine Liste des gleichen Typs. Prädikate werden verwendet, um zu bestimmen, ob ein Objekt ein bestimmtes Kriterium erfüllt und ob diese Funktion Elemente aus einer Liste gemäß einem Prädikat auszuwählen - wahr oder falsch. Danach wird eine Teilmenge der ursprünglichen Liste zurückgegeben. Ein Beispiel für eine Funktion mit dieser Signatur ist List.filter .


 ('a -> 'b) -> 'a list -> 'b list 

Die Funktion akzeptiert zwei Parameter: Konvertierung von Typ 'a nach Typ 'b und eine Liste vom Typ 'a . Der Rückgabewert ist eine Liste vom Typ 'b . Es ist anzunehmen, dass die Funktion jedes Element aus der Liste 'a nimmt und es unter Verwendung der als erster Parameter übergebenen Funktion in 'b konvertiert und dann die Liste 'b zurückgibt. In der Tat ist List.map der Prototyp einer Funktion mit einer solchen Signatur.


Suchen Sie nach Bibliotheksmethoden mit Signaturen


Funktionssignaturen sind sehr wichtig, um Bibliotheksfunktionen zu finden. F # -Bibliotheken enthalten Hunderte von Funktionen, was zunächst verwirrend sein kann. Im Gegensatz zu objektorientierten Sprachen können Sie ein Objekt nicht einfach durch einen Punkt eingeben, um alle zugehörigen Methoden zu finden. Wenn Sie jedoch die Signatur der gewünschten Funktion kennen, können Sie Ihre Suche schnell eingrenzen.


Sie haben beispielsweise zwei Listen und möchten eine Funktion finden, die sie zu einer kombiniert. Welche Signatur hätte die gewünschte Funktion? Es müsste zwei Listen als Parameter verwenden und eine dritte zurückgeben, alle vom gleichen Typ:


 'a list -> 'a list -> 'a list 

Gehen Sie nun zur MSDN-Dokumentationssite für das Listenmodul und suchen Sie nach einer ähnlichen Funktion. Es stellt sich heraus, dass es mit einer solchen Signatur nur eine Funktion gibt:


 append : 'T list -> 'T list -> 'T list 

Was du brauchst!


Native Typen für Funktionssignaturen definieren


Eines Tages möchten Sie Ihre eigenen Typen für die gewünschte Funktion definieren. Dies kann mit dem Schlüsselwort "Typ" erfolgen:


 type Adder = int -> int type AdderGenerator = int -> Adder 

In Zukunft können Sie diese Typen verwenden, um die Werte von Funktionsparametern zu begrenzen.


Beispielsweise wird eine zweite Deklaration aufgrund einer Einschränkung mit einem Umwandlungsfehler fallen. Wenn wir es entfernen (wie in der dritten Ankündigung), verschwindet der Fehler.


 let a:AdderGenerator = fun x -> (fun y -> x + y) let b:AdderGenerator = fun (x:float) -> (fun y -> x + y) let c = fun (x:float) -> (fun y -> x + y) 

Testen des Verständnisses von Funktionssignaturen


Verstehen Sie Funktionssignaturen gut? Überprüfen Sie selbst, ob Sie mit den folgenden Signaturen einfache Funktionen erstellen können. Vermeiden Sie die explizite Angabe von Typen!


 val testA = int -> int val testB = int -> int -> int val testC = int -> (int -> int) val testD = (int -> int) -> int val testE = int -> int -> int -> int val testF = (int -> int) -> (int -> int) val testG = int -> (int -> int) -> int val testH = (int -> int -> int) -> int 

Zusätzliche Ressourcen


Es gibt viele Tutorials für F #, einschließlich Materialien für diejenigen, die mit C # oder Java-Erfahrung kommen. Die folgenden Links können hilfreich sein, wenn Sie tiefer in F # einsteigen:



Es werden auch verschiedene andere Möglichkeiten beschrieben , um mit dem Lernen von F # zu beginnen .


Schließlich ist die F # Community sehr anfängerfreundlich. Bei Slack gibt es einen sehr aktiven Chat, der von der F # Software Foundation unterstützt wird, mit Anfängerräumen, an denen Sie frei teilnehmen können . Wir empfehlen Ihnen dringend, dies zu tun!


Vergessen Sie nicht, die Seite der russischsprachigen Community F # zu besuchen! Wenn Sie Fragen zum Erlernen einer Sprache haben, diskutieren wir diese gerne in Chatrooms:



Über Übersetzungsautoren


Übersetzt von @kleidemos
Übersetzungs- und redaktionelle Änderungen wurden durch die Bemühungen der russischsprachigen Community von F # -Entwicklern vorgenommen . Wir danken auch @schvepsss und @shwars für die Vorbereitung dieses Artikels zur Veröffentlichung.

Source: https://habr.com/ru/post/de433402/


All Articles