Einführung in F #, die blaue Pille

    [ Vorheriger Beitrag ]

    Einleitung


    Bild
    Dies ist die erwartete oder nicht sehr gute Fortsetzung. Heute schlucken wir die blaue Pille, die mit Stolz FP (Functional Programming) verkörpert, und tauchen tiefer in den funktionalen Teil von F # ein. Lassen Sie uns über Funktionen, Rekursion, Mustererkennung und einige andere interessante Dinge sprechen. Interessant? Dann schlucke die Pille und beginne den Tauchgang.


    Eintauchen


    Bild
    Zunächst möchte ich über die Ursprünge von FP, dem λ-Kalkül, sprechen. Immerhin war es diese mathematische Theorie, die von Alonzo Church entwickelt wurde, die den Grundstein für die funktionale Programmierung legte. Ich werde keinen langen und langweiligen Vortrag über Mathematik halten, ich werde es Ihnen nur kurz erzählen. Am Ende wird es dann möglich sein, jemanden mit seinem Wissen zu überraschen, nicht nur in F #, sondern auch in seinem mathematischen Hintergrund. Was ist eigentlich dieser schreckliche Name für den λ-Kalkül? Diese Kirche wollte die Mathematik so verallgemeinern, dass absolut alles in Form eines der Grundbegriffe dargestellt werden konnte - Funktionen. Darüber hinaus legt seine Theorie nahe, dass alles eine Funktion ist. „Was ist das Problem? Warum nicht das bestehende Funktionskonzept nutzen? “, Wird der Leser fragen. Tatsache ist, dass der Mathematiker Funktionen überall und überall verwenden wollte und es ihnen oft unangenehm ist, einen Namen zu geben.
    f(x) = x - 5
    f(6) = 6 - 5 = 1

    Dann kam Alonzo auf eine Notation, mit der Sie eine Funktion schreiben können, ohne einen Namen zu vergeben: In diesem Fall ist die Funktion absolut identisch mit dem vorherigen Beispiel, außer dass sie keinen Namen hat. Genauso können Sie den Wert des Arguments übergeben: Sie können auch eine andere λ-Funktion an die Parameter der λ-Funktion übergeben. Ich denke, die direkte Analogie zu den Lambda-Funktionen, die wir im vorherigen Artikel kennengelernt haben, ist deutlich sichtbar.
    λx.x-5


    (λx.x - 5) 6 = 6 - 5 = 1



    „Hör auf Unsinn zu reden! Mehr Code. "

    Bild
    Und ich bin fast fertig mit Mathe. Fast. Schließlich weiß jeder, was Rekursion ist? Das ist schön Wir werden jetzt eine rekursive Funktion schreiben (die sich selbst aufruft), um die Fibonacci-Zahlen zu berechnen. Fibonacci-Zahlen - eine Folge von Zahlen, wobei jede nachfolgende Zahl gleich der Summe der vorhergehenden Zahlen ist: Wenn Sie sie in der Sprache der Funktionen schreiben, sieht die Funktion, die die Fibonacci-Zahl anhand ihrer Zahl in der Folge zurückgibt, folgendermaßen aus: Und auf F # wie folgt :
    Fib = {0, 1, 1, 2, 3, 5, 8, ...}


    f(n) = f(n - 1) + f(n - 2)
    f(1) = 1
    f(2) = 1


    let rec fib n =
        match n with
        | 1 | 2 -> 1
        | n -> fib(n-1) + fib(n-2)
    

    Hier sind zwei neue Schlüsselwörter zu berücksichtigen:
    • rec - zeigt an, dass die Funktion rekursiv ist
    • match, with ist ein Pattern Matching-Mechanismus, mit dem C # - oder C / C ++ - Pattern Matching als Schalter für Steroide dargestellt werden kann

    Was passiert in einer Funktion? Tatsächlich ist alles sehr einfach: Wenn das Argument n 1 oder 2 ist, geben wir 1 zurück, andernfalls geben wir die Summe der beiden vorherigen Zahlen in der Sequenz zurück. Eigentlich bietet match viel mehr Möglichkeiten, aber wir werden etwas später darauf zurückkommen und zunächst die Haupttypen in F # betrachten.

    Typdeklaration

    Bild
    Um Ihren Typ zu deklarieren, müssen Sie seltsamerweise das Schlüsselwort type verwenden . Bisher können wir nichts Sinnvolles deklarieren, definieren Sie also einfach int neu:
    type myInt = int
    


    Tuples

    Bild
    Ein Tupel oder Tupel ist eine Art zusammengesetzter Typ in F #. Betrachten Sie die folgenden Beispiele, um schnell zu verstehen, was und wie:
    // тип int*int; в данном случае * - не умножение
    let point2d = (10, 5)
    // тип int*int*int
    let point3d = (1, 1, 2)
    // тип string*int
    let personAndId = ("Some Name", 3)
    let x2d = fst point2d
    let y2d = snd point2d
    

    Wie Sie dem Beispiel entnehmen können, ist ein Tupel eine Kombination aus zwei oder mehr Typen (nicht benannt, aber geordnet). point2d und point3d repräsentieren Punkte auf zwei- und dreidimensionalen Ebenen, personAndId - name und id. Um die Arbeit mit Tupeln zu vereinfachen, verfügt die F # -Standardbibliothek über zwei Funktionen: fst und snd, die das erste bzw. zweite Element des Tupels zurückgeben. Wie bekomme ich den dritten Artikel?
    // функция принимает в параметры кортеж; _ означает, что данное значение нам не интересно/может быть любым
    let third (_, _, t) = t
    

    Hier ist ein Beispiel für eine Tupeltypdeklaration:
    // объявляем тип-кортеж; первый элемент имеет тип string, второй int
    type personAndId = string*int
    // объявляем значение типа personAndId; при помощи оператора : мы явно указываем тип значения, привязываемого к идентификатору 
    let person:personAndId  = ("Habr", 1)
    


    Aufzeichnungen aka Aufzeichnungen

    Bild
    Natürlich geht es hier nicht um Aufzeichnungen. Records sind eine weitere Struktureinheit von F #. Sie unterscheiden sich von Tupeln darin, dass jedes Feld des Datensatzes einen Namen hat, der Inhalt kann jedoch ein beliebiger sein. Betrachten Sie die Syntax:
    type person = {name : string; surname : string; id : int}
    let sampleUser = {name = "Habra"; surname = "Habr"; id = 1}
    let sampleUserName = sampleUser.name
    

    Ich denke, dass keine Erklärung benötigt wird.

    Diskriminierte Gewerkschaften oder markierte Gewerkschaften

    Bild
    Die nächste strukturelle Einheit sind Assoziationen. Sie ermöglichen es Ihnen, Daten auf verschiedene Arten zu kombinieren. Hier ist ein Beispiel für einen einfachen Join:
    type Currency = 
        | Liter of float
        | Pint of float
    let liter = Liter 5.0
    let pint = Pint 10.0
    


    Mustervergleich oder Mustervergleich

    Bild
    Da der Leser bereits viel über die grundlegenden Struktureinheiten von F # weiß, können wir detaillierter auf die Mustererkennung eingehen. Match ähnelt in gewisser Weise einem Wechsel von Imperativ- und OO-Sprache, bietet jedoch viel mehr Möglichkeiten. Die einzige Einschränkung besteht darin, dass Vorlagen den gesamten Bereich möglicher Werte abdecken sollten. Ich möchte darauf hinweisen, dass es nur auf den ersten Blick unangenehm und absurd erscheint.
    // функция, определяющая четность числа
    let isOdd n = (n % 2 = 1)
    // функция, выводящая числа от 1 до 3 в словесном виде
    let oneToThree n = 
        match n with
        | 1 -> printfn "One"
        | 2 -> printfn "Two"
        | 3 -> printfn "Three"
        // для всех остальных значений используем следующий шаблон; тут так же возможно использовать _ вместо n
        | n -> printfn "Geather than three"
    // определим кортеж
    type Point = int * int
    // пример matching'а для кортежей
    let findZero point =
        match point with
        | (0, 0) -> printfn "x = 0 and y = 0"
        | (0, y) -> printfn "(0, %d)" y
        | (x, 0) -> printfn "(%d, 0)" x
        | _ -> printfn "x <> 0 and y <> 0"
    type Person =
        | NameOnly of string
        | IdOnly of int
        // в этом объединении один из членов - кортеж
        | IdAndName of string * int
    // пример matching'а для объединений
    let personInfo person = 
        match person with
        | NameOnly(name) -> printf "Name is %s" name
        | IdOnly(id) -> printf "Id is %d" id
        | IdAndName(name, id) -> printf "Name is %s and Id is %d?" name id
    // пример matching'a с дополнительными условиями
    let matchWithCondition x = 
        match x with
        | x when x >= 5. && x <= 10. -> printfn "x is between 5.0 and 10.0"
        | _ -> ()
    

    Eine ausführlichere Beschreibung finden Sie hier .

    Generische Typen

    Bild
    Nehmen wir an, wir brauchen eine Implementierung eines Binärbaums, der einen Typ hat, dh einen Baum von Zeichenfolgen, einen Baum von Ints usw. Typen in F # können wie folgt verallgemeinert werden:
    // обобщаемый тип помечается символом ' перед его названием
    type 'a BinaryTree =
        // узлом дерева будет являться кортеж из дерева и дерева, кхе-кхе
        | Node of 'a BinaryTree * 'a BinaryTree
        | Value of 'a
    (*
    многим другой синтаксис может показаться более знакомым и понятным:
    type BinaryTree<'a> =
        | Node of BinaryTree<'a> * BinaryTree<'a>
        | Value of 'a
    *)
    // так же, как и было с функциями и значениями, компилятор автоматически определит тип дерева
    let tree1 = 
        Node(
            Node (Value 1, Value 2),
            Node (Value 5, Value 100)
            )
    let tree2 = 
        Node(
            Node (Value "Binary tree", Value "in F#"),
            Node (Value "is so", Value "Simple")
            )
    

    Ja, der verallgemeinerte Binärbaum auf F # sieht genauso aus.

    Fazit


    Bild
    Na was soll ich sagen, ich hoffe ich habe es nicht vergeblich versucht und viele werden diesen Beitrag auch mögen. Das nächste Mal werden wir weiterhin die blauen Pillen schlucken, über Listen und Sequenzen, Curry-Funktionen und vieles mehr sprechen. Danke fürs Lesen.

    Was zu lesen

    Erstens, msdn , finden Sie dort eine detailliertere Beschreibung von vielem, worüber heute gesprochen wurde.
    Zweitens ist funktionale Programmierung für die reale Welt (Tomas Petricek, Jon Skeet) ein Buch für diejenigen, die an einen zwingenden Ansatz zur Lösung von Problemen gewöhnt sind. Einige Beispiele aus diesem Beitrag wurden teilweise von dort übernommen.

    Für Wenige


    Fibonacci

    In der Tat ist der Code, der im obigen Beitrag angegeben wurde, schrecklich. Aber dann ist es leicht zu verstehen. Und für diejenigen, die einen funktionaleren Ansatz sehen möchten, gebe ich ein kompetenteres Beispiel (danke, onikiychuka ):
    let fib = Seq.unfold (fun state ->Some(fst state + snd state, (snd state, fst state + snd state))) (1,1)
    fib |> Seq.take 20
    

    Wie oben erwähnt, werden wir solche Funktionen beim nächsten Mal genauer analysieren.

    Mustervergleich

    Geben Sie Ausdrücke ein
    let second (x, y) = y
    let getValueFromOption (Some value) = value
    

    stellen auch einen Mustervergleichsmechanismus dar. Danke jack128 und onikiychuka .

    Jetzt auch beliebt: