Funktionale CoffeeScript-Programmierung mit der Bibliothek f_context

    Diejenigen, die mit funktionalen Programmiersprachen konfrontiert sind, kennen wahrscheinlich dieses Design:
      fact(0) -> 1
      fact(N) -> N * fact(N - 1)
    

    Dies ist eines der klassischen Beispiele für Phasenübergänge - Faktorielle Berechnung.
    Jetzt können Sie dies in CoffeeScript mit der Bibliothek f_context tun, indem Sie den Code einfach in f_context -> einschließen , zum Beispiel:
      f_context ->
        fact(0) -> 1
        fact(N) -> N * fact(N - 1)
    


    So funktioniert es
    Weitere Beispiele

    Was die Bibliothek kann


    Module
    Pattern Matching
    Destructuring
    Guards
    Variable _ und Matching Same Arguments

    Beispiele aus der Praxis


    Modularität:

    Standardmäßig gibt f_context das generierte Modul zurück, d. H. Es kann in eine Variable geschrieben werden:
    examples = f_context ->
      f_range(I) ->
        f_range(I, 0, [])
      f_range(I, I, Accum) -> Accum
      f_range(I, Iterator, Accum) ->
        f_range(I, Iterator + 1, [Accum..., Iterator])
    examples.f_range(10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    Mit der Modul- Direktive können Sie ein Modul sofort in einen globalen Bereich
    wie window oder global einfügen :
    f_context ->
      module("examples")
      f_range(I) ->
        f_range(I, 0, [])
      f_range(I, I, Accum) -> Accum
      f_range(I, Iterator, Accum) ->
        f_range(I, Iterator + 1, [Accum..., Iterator])
    examples.f_range(10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    


    Mustervergleich

    was ist das und warum wird es gebraucht
    Beispiel:
      matching_example_1("foo") -> "foo matches"
      matching_example_1("bar") -> "bar matches"
      matching_example_1(1) -> "1 matches"
      matching_example_1(true) -> "true matches"
      matching_example_1(Str) -> "nothing matches, argument: #{Str}"
    

    Ergebnis:
      matching_example_1("foo") #returns "foo matches"
      matching_example_1("bar") #returns "bar matches"
      matching_example_1(1) #returns "1 matches"
      matching_example_1(true) #returns "true matches"
      matching_example_1("baz") #returns "nothing matches, argument: baz"
    



    Zerstörung

    was ist es und warum wird es benötigt
    Beispiele:
      test_destruct_1([Head, Tail...]) -> {Head, Tail}
      test_destruct_1_1([Head, Head1, Tail...]) -> {Head, Head1, Tail}
    


      test_destruct_2([Head..., Last]) -> {Head, Last}
      test_destruct_2_1([Head..., Last, Last1]) -> {Head, Last, Last1}
    


      test_destruct_3([Head, Middle..., Last]) -> {Head, Middle, Last}
      test_destruct_3_1([Head, Head2, Middle..., Last, Last2]) -> {Head, Head2, Middle, Last, Last2}
    

    Ergebnis:
      test_destruct_1([1,2,3]) #returns {Head: 1, Tail: [2,3]}
      test_destruct_1_1([1,2,3,4]) #returns {Head: 1, Head1: 2, Tail: [3,4]}
      test_destruct_2([1,2,3]) #returns {Head: [1,2], Last: 3}
      test_destruct_2_1([1,2,3,4]) #returns {Head: [1,2], Last: 3, Last1: 4}
      test_destruct_3([1,2,3,4]) #returns {Head: 1, Middle: [2,3], Last: 4}
      test_destruct_3_1([1,2,3,4,5,6]) #returns {Head: 1, Head2: 2, Middle: [3,4], Last: 5, Last2: 6}
    



    Wachen

    Was es ist und warum es benötigt wird. Die
    Guards werden durch die where- Direktive angegeben (% condition%) .
    In Guards können Sie einen flexibleren Vergleich festlegen. Beispiel für die Berechnung der Fibonacci-Reihe:
      #без гвардов
      fibonacci_range(Count) ->
        fibonacci_range(Count, 0, [])
      fibonacci_range(Count, Count, Accum) -> Accum
      fibonacci_range(Count, 0, Accum) ->
        fibonacci_range(Count, 1, [Accum..., 0])
      fibonacci_range(Count, 1, Accum) ->
        fibonacci_range(Count, 2, [Accum..., 1])
      fibonacci_range(Count, Iterator, [AccumHead..., A, B]) ->
        fibonacci_range(Count, Iterator + 1, [AccumHead..., A, B, A + B])
    

      #с гвардами
      fibonacci_range(Count) ->
        fibonacci_range(Count, 0, [])
      fibonacci_range(Count, Count, Accum) -> Accum
      fibonacci_range(Count, Iterator, Accum) where(Iterator is 0 or Iterator is 1) ->
        fibonacci_range(Count, Iterator + 1, [Accum..., Iterator])
      fibonacci_range(Count, Iterator, [AccumHead..., A, B]) ->
        fibonacci_range(Count, Iterator + 1, [AccumHead..., A, B, A + B])
    



    Variable _ und übereinstimmende Argumente

    Die Variable _ wird verwendet , um „drop“ die Argumente, wenn ihr Wert ist nicht wichtig, aber es ist wichtig , die Anzahl der Argumente
    Beispiel die Funktion aller realisieren:
    f_all([Head, List...], F) ->
      f_all(List, F, F(Head))
    f_all(_, _, false) -> false
    f_all([], _, _) -> true
    f_all([Head, List...], F, Memo) ->
      f_all(List, F, F(Head))
    

    Wenn Sie dieselben Namen für die Argumente festlegen, werden diese automatisch verglichen.
    Eine beispielhafte Implementierung der Bereichsfunktion:
    f_range(I) ->
      f_range(I, 0, [])
    f_range(I, I, Accum) -> Accum
    f_range(I, Iterator, Accum) ->
      f_range(I, Iterator + 1, [Accum..., Iterator])
    



    Beispiele aus dem Leben

    Manchmal ist es viel bequemer, einen bestimmten Algorithmus mithilfe von Rekursion zu implementieren.
    Zum Beispiel Funktionen reduzieren und schnelle Art in einem funktionalen Stil:
    f_context ->
      f_reduce(List, F) ->
        f_reduce(List, F, 0)
      f_reduce([], _, Memo) -> Memo
      f_reduce([X, List...], F, Memo) ->
        f_reduce(List, F, F(X, Memo))
    

    f_context ->
      f_qsort([]) -> []
      f_qsort([Pivot, Rest...]) ->
        [
          f_qsort((X for X in Rest when X < Pivot))...,
          Pivot,
          f_qsort((Y for Y in Rest when Y >= Pivot))...
        ]
    

    Meiner Meinung nach ist es einfacher und klarer als ihre zwingenden Gegenstücke.


    Wie funktioniert das?


    Kehren wir zum allerersten Beispiel in diesem Artikel zurück - der Fakultätsberechnung.
      fact(0) -> 1
      fact(N) -> N * fact(N - 1)
    

    In CoffeeScript ist dies ein absolut gültiges Konstrukt.

    Ein Beispiel ist komplizierter, wenn die Anzahl der Elemente in einer Liste in einem funktionalen Stil gezählt wird:
      count(List) ->
        count(List, 0)
      count([], Iterator) -> Iterator
      count([Head, List...], Iterator) ->
        count(List, Iterator + 1)
    

    Und das ist auch gültiger CoffeeScript-Code.
    Auf diese Weise können wir im für funktionale Sprachen üblichen Stil direkt auf Kaffee schreiben, ohne einen Precompiler verwenden zu müssen.

    Der Klarheit halber werde ich weiterhin auf das Beispiel derselben Fakultät eingehen. Also
    dieser Code:
      fact(0) -> 1
      fact(N) -> N * fact(N - 1)
    

    übersetzt in js wie folgt:
      fact(0)(function() {
        return 1;
      });
      fact(N)(function() {
        return N * fact(N - 1);
      });
    

    Es kann sogar ausgeführt werden, aber bei "ReferenceError" -Fehlern werden diese Tatsache und N nicht deklariert.
    Sie können diesen Code in eine Wrapper-Funktion einschließen, sodass er als Argument übergeben wird
      function_wrapper ->
        fact(0) -> 1
        fact(N) -> N * fact(N - 1)
    

    in js bekommen wir folgendes:
    function_wrapper(function() {
        fact(0)(function() {
          return 1;
        });
        fact(N)(function() {
          return N * fact(N - 1);
        });
    });
    

    Jetzt kann function_wrapper die im Argument übergebene Funktion analysieren
    und alle fehlenden Variablen an sie übergeben. So etwas in der Art:
    var function_wrapper = function(fn){
      var fn_body = fn.toString().replace(/function.*\{([\s\S]+)\}$/ig, "$1");
      var new_function = Function.apply(null, fn_body, /*именованные аргументы*/ 'fact', 'N');
      var fact_stub = function(){
        return function(){};
      };
      // маркируем N как переменную
      var N_stub = function(){};
      N_stub.type = "variable";
      N_stub.name = "N";
      new_function(fact_stub, N_stub)
    }
    

    Nun wird der Code fehlerfrei ausgeführt, tut aber bisher nichts.
    Als nächstes steht fact_stub zur Verfügung , mit dem auch die Argumente analysiert und
    der lokale Zweig der aktuellen Funktion generiert werden kann. Für faktorielles wird es in etwa so aussehen:

    Nach dem Parsing - Baum gebaut ist, können Sie leicht sgerenirovat Modul können und es durch die gleichen schafft Function.apply
    Körper Modul sollte wie folgt aussehen:
    var f_fact_local_0 = function(){
      return 1;
    };
    var f_fact_local_1 = function(N){
      return N * f_fact(N - 1);
    };
    var f_fact = function(){
      if(arguments[0] === 0){
        return f_fact_local_0();
      }
      return f_fact_local_1(arguments[0]);
    };
    return {
      f_fact: f_fact
    };
    


    Tatsächlich ist alles etwas komplizierter, aber hier habe ich versucht, die Grundprinzipien der Arbeit zu formulieren.
    Wenn der Artikel nicht klar ist - schreibe, werde ich versuchen, ihn zu beheben.

    Die Bibliothek selbst ist hier: github.com/nogizhopaboroda/f_context

    Kommentare sind willkommen. Wie Feature-Anfragen.

    Nur registrierte Benutzer können an der Umfrage teilnehmen. Bitte komm rein .

    Ist der Artikel klar?

    • 54,9% verstehen 28
    • 25,4% verstehen nicht 13
    • 33,3% Worum geht es? 17

    Jetzt auch beliebt: