Pensons à ce qu'est la programmation déclarative en Java et Elm (partie 1)

Effet

Connaissez-vous le terme programmation déclarative? C'est l'un des styles lors de la programmation. Jetons un coup d'œil à Wikipedia. Dans de nombreux cas, je pense que ce dernier paradigme ou langage de programmation concret est souvent imaginé, donc cet article prend (probablement) en charge Java, qui ne prend pas en charge la programmation déclarative. Je voudrais faire une comparaison avec Elm. Elm est un langage spécialisé pour la programmation frontale Web. Alors, quelle est exactement la programmation déclarative? Dans Wikipedia, la programmation se fait en déclarant la nature de la cible et non la méthode de traitement. Il y a une description. Java, qui sera la cible de cette fois, sera programmé principalement en utilisant le type procédural. Jetons un coup d'œil à la motivation de cet article, puis utilisons le code réel comme exemple pour avoir une idée de la différence spécifique.

La programmation déclarative (anglais: programmation déclarative) est le nom du paradigme de programmation, mais elle a deux significations principales. La première signification est un paradigme pour la programmation en déclarant la nature de l'objet, pas la méthode de traitement. La deuxième signification est un terme général pour la programmation fonctionnelle pure, la programmation logique et la programmation par contraintes. Ce dernier a la première nature (plus ou moins).

Cette motivation est pour ceux qui font habituellement de la programmation procédurale de s'habituer à la programmation déclarative. J'espère que ce sera une référence pour changer le cerveau pour une transition progressive. Cet article est divisé en la première partie et la deuxième partie, et la première partie consiste à créer progressivement le code de programmation déclarative à travers un exemple simple qui génère n'importe quelle étape de 99. Enfin, je vais expliquer brièvement le code dans Elm. Dans la deuxième partie, je voudrais présenter des techniques de programmation déclarative en utilisant spécifiquement les caractéristiques de quel type de langage.

Programme quatre-vingt-dix-neuf

Maintenant, écrivons un programme qui produit n'importe quelle étape de quatre-vingt-dix-neuf.

Dans les spécifications de ce programme, spécifiez un nombre quelconque de colonnes (3 colonnes sont spécifiées par des variables pour simplification), et la sortie sur une ligne séparée par des espaces demi-largeur. C'est le contenu du programme que nous allons faire cette fois. Tout d'abord, si vous l'écrivez vous-même sans regarder le code, vous pouvez vérifier si vous êtes procédural ou pédagogique.

Le premier est le code lorsque le problème est interprété tel quel et écrit dans le style Java classique. Il définit la méthode printNMultiTable, une fonction qui génère une colonne arbitraire sur une ligne. Je pense que cela peut être exprimé en mots comme suit.

  1. Déclarez la variable d'état (compteur) i et définissez la valeur initiale sur 1.
  2. Si i n'est pas 9, insérez un espace demi-largeur après le résultat de i * n et sortez sur la sortie standard.
  3. Si i vaut 9, sortie i * n vers la sortie standard
  4. Ajoutez 1 à la variable d'état à mettre à jour et répétez le processus ci-dessus (2, 3) jusqu'à ce que i devienne 9.
  5. Lorsque l'itération est terminée, sortez un saut de ligne sur la sortie standard et quittez la méthode.

Ce code est une programmation procédurale des manières suivantes:

En d'autres termes, le contenu que l'ordinateur traite et le contenu qui calcule les quatre-vingt-dix-neuf sont mélangés.

 public class Test {
    public void printNMultiTable(int n) {
      for (int i = 1; i <= 9; i++) {
        if (i != 9) {
          System.out.print(i * n + " ");
        } else {
          System.out.print(i * n);
        }
      }
      System.out.println();
    }
  
    public Test() {
      printNMultiTable(3);
      // => 3 6 9 12 15 18 21 24 27
    }
  
    public static void main(String args[]) {
      new Test();
    }
  }  

Avant de passer à l'étape suivante, j'aimerais examiner les avantages et les inconvénients du style procédural ci-dessus. Le type procédural est un code qui montre clairement que le résultat calculé est sorti vers la sortie standard, qui est l'objectif final. Cela se traduira par de bonnes performances s'il est bien fait. D'autre part, quelle est cette sortie d'espace quand quelqu'un qui ne connaît pas les spécifications voit ce code? Quel est le dernier saut de ligne? Doit être déduit. L'écriture de code de test est un meilleur moyen d'éviter les inférences et de laisser des spécifications. Cependant, le code de test du résultat qui est sorti en standard n'est pas facile. Cela ne vaut pas le coût car cela nécessite des efforts qui s'écartent de l'objectif initial, tels que la vérification visuelle à l'aide d'un débogueur, la sortie dans un fichier et la prise d'un diff du fichier. La programmation déclarative résout-elle ces problèmes? Je voudrais maintenant passer à l’étape suivante.

Dans l'étape suivante, nous nous concentrerons sur le résultat du calcul, pas sur l'objectif final. Plus précisément, elle est divisée en deux méthodes: la méthode nMultiTable qui renvoie List qui représente la troisième étape, et la méthode printList qui génère le contenu de la List séparé par des espaces. En faisant cela, vous pouvez écrire un code de test que l'appel de méthode appelé nMultiTable (3) est une expression et le résultat évalué est égal à la liste représentant la 3e étape. Le problème avec cette étape est que la partie procédurale de la méthode elle-même reste et que le code est encore difficile à déduire.


import java.util.List;
 import java.util.ArrayList;
 
 public class Test {
   public List<Integer> nMultiTable(int n) {
     final List<Integer> table = new ArrayList<>();
 
     for (int i = 1; i <= 9; i++) {
       table.add(i * n);
     }
 
     return table;
   }
 
   public void printList(List<Integer> list) {
     for (int i = 0; i < list.size(); i++) {
       if (i != list.size() - 1) {
         System.out.print(list.get(i) + " ");
       } else {
         System.out.print(list.get(i));
       }
     }
     System.out.println();
   }

    public Test() {
     List<Integer> threeTable = nMultiTable(3);
     // == Arrays.asList(3, 6, 9, 12, 15, 18, 21, 24, 27)
     printList(threeTable);
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }
 

Améliorons le code de nMultiTable. La variable d'état i pour tourner la boucle for a généré une chaîne de nombres basée sur quatre-vingt-dix-neuf. La solution la plus simple pour rendre ce déclaratif est de lister directement les nombres de 1 à 9.

import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 
 public class Test {
   public List<Integer> nMultiTable(int n) {
     final List<Integer> table = new ArrayList<>();
     final List<Integer> base = Arrays.asList(1, 2, 3, 4, 5, 5, 6 ,7, 8, 9);
 
     for (int x : base) {
       table.add(x * n);
     }
 
     return table;
   }
 
   public void printList(List<Integer> list) {
     for (int i = 0; i < list.size(); i++) {
       if (i != list.size() - 1) {
         System.out.print(list.get(i) + " ");
       } else {
         System.out.print(list.get(i));
       }
     }
     System.out.println();
   }

   public Test() {
     List<Integer> threeTable = nMultiTable(3);
     printList(threeTable);
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }

Cependant, l'énumération à la main était une solution trop agressive, j'ai donc utilisé une méthode appelée IntStream # rangeClosed pour créer un flux dans la plage et le convertir en une liste avec collect. Quelle est la différence entre ceci et tourner le premier pour? Cette fois, c'était très simple, donc il n'y a pas de grande différence, mais comme cette base n'est pas une réécriture de l'état mais une liste concrète, cela peut aussi être considéré comme une cible de test. De plus, étant donné que la méthode garantit qu'une liste de 1 à 9 est générée, il est possible d'éviter des erreurs telles que des erreurs d'incrémentation en remplaçant celle dans laquelle pour réécrit i par l'extension pour.

import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.stream.IntStream;
 import java.util.stream.Collectors;
 
 public class Test {
   public List<Integer> nMultiTable(int n) {
     final List<Integer> table = new ArrayList<>();
     final List<Integer> base = IntStream.rangeClosed(1, 9).boxed().collect(Collectors.toList());
 
     for (int x : base) {
       table.add(x * n);
     }
 
     return table;
   }
 
   public void printList(List<Integer> list) {
     for (int i = 0; i < list.size(); i++) {
       if (i != list.size() - 1) {
         System.out.print(list.get(i) + " ");
       } else {
         System.out.print(list.get(i));
       }
     }
     System.out.println();
   }

   public Test() {
     List<Integer> threeTable = nMultiTable(3);
     printList(threeTable);
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }

J'avais l'habitude de multiplier la séquence numérique de base par le nombre de quatre-vingt-dix-neuf colonnes, mais il n'est pas toujours nécessaire de conserver la séquence numérique. Appelez la méthode IntStream # mapToObj pour le convertir en un Stream qui est le résultat de la multiplication de tous les éléments de la colonne numérique de base. Puisqu'il ne s'agit pas d'un article d'introduction sur Stream, veuillez vous référer à la référence etc. pour la signification spécifique.

import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.stream.IntStream;
 import java.util.stream.Collectors;
 
 public class Test {
   public List<Integer> nMultiTable(int n) {
     return IntStream.rangeClosed(1, 9).mapToObj(x -> x * n).collect(Collectors.toList());
   }
 
   public void printList(List<Integer> list) {
     for (int i = 0; i < list.size(); i++) {
       if (i != list.size() - 1) {
         System.out.print(list.get(i) + " ");
       } else {
         System.out.print(list.get(i));
       }
     }
     System.out.println();
   }
 
   public Test() {
     List<Integer> threeTable = nMultiTable(3);
     printList(threeTable);
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }

Maintenant que nous l'avons fait en une seule ligne, supprimons la méthode nMultiTable.

import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.stream.IntStream;
 import java.util.stream.Collectors;
 
 public class Test {
 
   public void printList(List<Integer> list) {
     for (int i = 0; i < list.size(); i++) {
       if (i != list.size() - 1) {
         System.out.print(list.get(i) + " ");
       } else {
         System.out.print(list.get(i));
       }
     }
     System.out.println();
   }
 
   public Test() {
     int n = 3;
     List<Integer> threeTable = IntStream.rangeClosed(1, 9).mapToObj(x -> x * n).collect(Collectors.toList());
     printList(threeTable);
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }

Ensuite, regardons la printList. Sortie standard du contenu de List séparé par des espaces demi-largeur. Si vous pensez à ce processus de manière déclarative, on peut dire que c'est pour convertir une chaîne de nombres en une chaîne de chaînes, puis en une chaîne séparée par demi-largeur. String # join est une méthode qui transforme la liste en une chaîne séparée par une chaîne spécifiée et convient à la personnalisation. Utilisez printableList au lieu de printList. Lors de l'appel de mapToObj, le threeTable mentionné précédemment doit également être converti en chaîne de caractères.

import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.stream.IntStream;
 import java.util.stream.Collectors;
 
 public class Test {
 
   public String printableList(List<String> list) {
       return String.join(" ", list);
   }
 
   public Test() {
     int n = 3;
     List<String> threeTable = IntStream.rangeClosed(1, 9).mapToObj(x -> String.valueOf(x * n)).collect(Collectors.toList());
     System.out.println(printableList(threeTable));
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }

Si vous y réfléchissez bien, il semble que vous puissiez la convertir en chaîne de caractères lorsque vous la terminez avec la méthode collect. Appelons la méthode de jonction Collectors #. J'ai pu le convertir en code procédural en une seule ligne!

import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.stream.IntStream;
 import java.util.stream.Collectors;
 
 public class Test {
 
   public Test() {
     int n = 3;
     String threeTable = IntStream.rangeClosed(1, 9).mapToObj(x -> String.valueOf(x * n)).collect(Collectors.joining(" "));
     System.out.println(threeTable);
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }

Mais attendez. Des omissions de code excessives réduisent le code de test, ce qui entraîne la sécurité globale et inversement la lisibilité du code. Dans la programmation pratique, gardez cet équilibre à l'esprit lors de la refactorisation. Renvoyez la méthode nMultiTable afin que vous puissiez écrire du code de test pour n'importe quel nombre de colonnes. D'un autre côté, les chaînes séparées par des espaces sont juste traitées pour la sortie finale, nous avons donc décidé qu'aucun test n'était nécessaire. Ce jugement n'est pas la seule bonne réponse. Concevons en tenant compte des exigences de mise en œuvre.

import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.stream.IntStream;
 import java.util.stream.Collectors;
 
 public class Test {
 
   public List<Integer> nMultiTable(int n) {
     return IntStream.rangeClosed(1, 9).mapToObj(x -> x * n).collect(Collectors.toList());
   }
 
   public Test() {
     final int n = 3;
     final List<Integer> threeTable = nMultiTable(3);
     // == Arrays.asList(3, 6, 9, 12, 15, 18, 21, 24, 27)
     String threeTableText = threeTable.stream().map(String::valueOf).collect(Collectors.joining(" "));
     // == "3 6 9 12 15 18 21 24 27"
     System.out.println(threeTableText);
   }
 
   public static void main(String args[]) {
     new Test();
   }
 }

Comme je l'ai noté à l'étape précédente, la programmation déclarative == ne programme pas avec StreamAPI. De plus, la programmation déclarative n'est pas une bonne technique. Il existe de nombreux cas où il est parfois préférable de faire de la programmation procédurale. En particulier, Java est à l'origine un langage qui prend en charge la POO basée sur des procédures, il existe donc de nombreux cas où d'excellentes méthodes procédurales sont fournies. Par conséquent, passons à la programmation déclarative en faisant attention à l'équilibre pour que les moyens ne soient pas le but.

Programmation déclarative avec Elm

Jetons un coup d'œil à l'exemple Elm, qui prend principalement en charge la programmation déclarative. Une grande arme si vous voulez itérer est un appel récursif. let définit l'expression nécessaire pour finalement renvoyer la valeur. In in décrit une expression pour renvoyer la valeur finale. Cette formule représente la propriété elle-même et est le résultat de la propriété représentée par la valeur. Dans la deuxième partie, je présenterai diverses fonctions Elm, mais en termes simples, toutes sont des formules. Les expressions, contrairement aux instructions, renvoient toujours une valeur. Vous devez donc tout écrire exactement, pas une expression ambiguë qui pourrait renvoyer un résultat. Dans l'exemple ci-dessous, le IF est une expression (la clause else ne peut pas être omise car elle est stricte). Le contenu de if est le suivant.

table (renvoie List) lorsque x vaut 10. Cela représente le résultat final Sinon, la liste commence récursivement par n * x (n est le nombre de colonnes et x est l'élément de la colonne numérique de base) et ce dernier élément est x + 1.

En d'autres termes, dans le cas de 3 étapes

(3 * 1) :: (3 * 2) :: (3 * 3) ... (3 * 9) :: [] == [3, 6, 9, ..., 27]

Ce sera.

Dans in, x commence à 1 et est d'abord une liste vide, c'est donc une expression explicite. Dans threeTableText, la liste à trois colonnes est convertie en une liste de chaînes et convertie en une chaîne séparée par des espaces. C'est le même que l'exemple Java.

 module Test exposing (..)
 
 import Html exposing (text)
 
 
 nMultiTable : Int -> List Int
 nMultiTable n =
     let
         nMulti x table =
             if x == 10 then
                 table
             else
                 (n * x) :: nMulti (x + 1) table
     in
         nMulti 1 []
 
 
 main =
     let
         n =
             3
 
         threeTable =
             nMultiTable n
 
         threeTableText =
             threeTable |> List.map toString |> String.join " "
     in
         text threeTableText

Bien sûr, vous pouvez faire la même chose que l'API Stream, et le code final a presque la même signification que le code Java final. Cependant, contrairement à Java, la liste par défaut contient des méthodes utiles pour faire beaucoup de programmation déclarative sans avoir à afficher une structure de données spéciale comme Stream. Par conséquent, le code est très simple.


module Test exposing (..)
 
 import Html exposing (text)
 
 
 nMultiTable : Int -> List Int
 nMultiTable n =
     List.range 1 9 |> List.map (\x -> x * n)
 
 
 main =
     let
         n =
             3
 
         threeTable =
             nMultiTable n
 
         threeTableText =
             threeTable |> List.map toString |> String.join " "
     in
         text threeTableText

Recommended Posts

Pensons à ce qu'est la programmation déclarative en Java et Elm (partie 1)
Ce que j'ai appris en Java (partie 4) Branchement conditionnel et répétition
Pensez aux différences entre les fonctions et les méthodes (en Java)
Qu'est-ce qu'un extrait de code en programmation?
Ce que j'ai appris en Java (partie 1) Flux de développement Java et présentation
Qu'est-ce qu'une classe en langage Java (3 /?)
Utilisez OpenCV_Contrib (ArUco) avec Java! (Partie 2-Programmation)
Qu'est-ce qu'une classe en langage Java (1 /?)
Qu'est-ce que Java et l'environnement de développement (MAC)
Qu'est-ce qu'une classe en langage Java (2 /?)
Quelle est la méthode principale en Java?
Qu'est-ce que 'java
Qu'est-ce que Java <>?
Ce que j'ai appris en Java (partie 2) Que sont les variables?
Qu'est-ce que 'java
[LeJOS] Programmons mindstorm-EV3 avec Java [Construction de l'environnement partie 2]
[JAVA] Quelle est la différence entre interface et abstract? ?? ??
Ce que j'ai appris en Java (partie 3) Déclaration d'exécution des instructions
[# 2 Java] Quelle est l'orientation de l'objet que vous entendez souvent?
Pensez au problème JAVA = JAVAscript (nécessaire à l'avenir)
Programmation par contraintes en Java
Qu'est-ce que l'encapsulation Java?
Qu'est-ce que la technologie Java?
Qu'est-ce que Java API-java
Tout sur la programmation Java
[Java] Qu'est-ce que flatMap?
[Java] Qu'est-ce que ArrayList?
JSON en Java et Jackson Partie 1 Renvoyer JSON à partir du serveur
[LeJOS] Programmons mindstorm-EV3 avec Java [Construction de l'environnement première partie]
Qu'est-ce que la classe LocalDateTime? [Java débutant] -Date et classe d'heure-
Que s'est-il passé dans «Java 8 to Java 11» et comment créer un environnement
StringUtils.isNotBlank est pratique pour un jugement vide et un jugement nul en java
Road to Java Engineer Part2 Quel type de langage est Java?
Concours de programmation AtCoder dwango B à résoudre en Ruby, Perl et Java
L'évaluation des courts-circuits est-elle vraiment rapide? Différence entre && et & en Java
Implémentons la condition que la circonférence et l'intérieur de la forme Ougi soient inclus dans Java [Partie 2]
Implémentons la condition que la circonférence et l'intérieur de la forme Ougi soient inclus dans Java [Partie 1]
Qu'est-ce que l'assertion Java? Résumé.
Ce que j'ai recherché sur Java 8
Ce que j'ai recherché sur Java 6
Programmation Java incroyable (arrêtons-nous)
Java et Iterator Part 1 External Iterator Edition
Ce que j'ai recherché sur Java 9
Apache Hadoop et Java 9 (partie 1)
[Java] À propos de String et StringBuilder
Qu'est-ce qu'une collection Java?
Quelle révolution se produit dans la programmation
Ce que j'ai recherché sur Java 7
[Java] Qu'est-ce que jaee j2ee?
[Java] Qu'est-ce que l'héritage de classe?
À propos du package Java et de l'importation
À propos de la classe abstraite Java
[Notions de base Java] Qu'est-ce que la classe?
Qu'est-ce que l'analyse Java Escape?
Qu'est-ce que les microservices et les frameworks de microservices
Ce que j'ai recherché sur Java 5