Au début, j'ai résumé la programmation orientée objet et ses fonctionnalités, mais je n'ai pas pu bien le faire et j'ai abandonné.
Donc, au lieu de cela, j'ai pensé qu'il serait préférable de créer une tâche simple, de la programmer concrètement et de la comparer, alors j'ai décidé de commencer par un simple programme d'addition.
Comme langage, j'ai choisi Pascal (Pascal standard) comme langage orienté procédure, Java comme langage orienté objet et Haskell comme langage fonctionnel.
Ce choix de langue est complètement un passe-temps. Je ne peux pas bien le dire, mais j'aime le langage à paradigme unique, à typage statique, qui a tendance à avoir une longue syntaxe et qui semble un peu "spongieux".
L'addition ne calcule pas simplement en une seule ligne, mais accepte les entrées de l'utilisateur et définit la fonction comme suit.
1.Tout d'abord, le message "Entrez un numéro. Il se termine par une ligne vide." S'affiche.
2. 「>S'affiche et attend une entrée. Si vous entrez un nombre et coupez une ligne, il sera enregistré
3.encore">S'affiche et attend une entrée. Répétez jusqu'à un saut de ligne sans entrer de caractères.
4.Si un saut de ligne vide se produit, le résultat de l'addition de la somme de tous les nombres saisis jusqu'à présent est affiché. "Le total est xxx" s'affiche.
5.Ignorez les entrées qui incluent des caractères autres que des nombres.
En fait, je pense qu'un échantillon un peu plus compliqué fera ressortir l'individualité de chaque langue, mais la première étape est la suivante.
L'image réelle de l'opération est comme ceci.
Veuillez saisir le numéro. Il se termine par une ligne vierge.
>10
>20
>4
>
Le total est de 34.
Cet article est destiné aux débutants qui ont appris un langage de programmation et peuvent comprendre le branchement conditionnel et le traitement en boucle.
Considérons ici le Pascal standard. Le Pascal standard a peu de fonctionnalités et ne peut pas être compilé de manière fractionnée, mais je pense qu'il convient toujours à l'apprentissage de la programmation, en particulier les bases des algorithmes d'apprentissage.
Tout d'abord, envisagez de mettre en œuvre la routine principale comme suit:
program CalcSumApp;
type
ItemPtr = ^Item;
Item =
record
next: ItemPtr;
text: ShortString;
end;
//Définition de diverses fonctions
var
lines : ItemPtr;
sum : Integer;
begin
writeln('Veuillez saisir le numéro. Il se termine par une ligne vierge.');
lines := readLines();
sum := sumOfLines(lines);
writeln('Le total est' + toStr(sum) + 'est.');
disposeAll(lines);
end.
Tout d'abord, nous définissons un pointeur appelé ItemPtr. En fait, en Pascal standard (Pascal ISO 7185), un tableau de longueur variable ne peut pas être utilisé, nous définissons donc ici un Node qui ne peut stocker qu'une seule valeur numérique appelée Item et la saisir sous la forme d'un pointeur. Modifié pour stocker la valeur numérique.
La routine principale (la partie ci-dessous begin
) ressemble à ceci:
Le message s'affiche sur la première ligne et la valeur numérique (plusieurs lignes) est acceptée sur la deuxième ligne.
Ensuite, sur la 3ème ligne, le total des valeurs numériques saisies est calculé, et sur la 4ème ligne, le résultat est affiché. toStr
est une fonction qui convertit un entier en chaîne, mais ce n'est pas non plus dans le pascal standard, donc il est implémenté dans le programme.
La cinquième ligne libère de la mémoire. Vous devez relâcher le pointeur vous-même lorsque vous avez terminé.
Maintenant, considérons l'implémentation de readLines
dans la partie d'entrée.
function readLines(): ItemPtr;
var
result: ItemPtr;
prevItemPtr: ItemPtr;
currItemPtr: ItemPtr;
line: ShortString;
begin
result := nil;
prevItemPtr := nil;
while true do
begin
write('>');
readln(line);
if line <> '' then
begin
if prevItemPtr = nil then
begin
new(prevItemPtr);
prevItemPtr^.text := line;
prevItemPtr^.next := nil;
result := prevItemPtr;
end
else
begin
new(currItemPtr);
currItemPtr^.text := line;
currItemPtr^.next := nil;
prevItemPtr^.next := currItemPtr;
prevItemPtr := currItemPtr;
end;
end
else
break;
end;
readLines := result;
end;
Entourez le tout dans une boucle while et faites-en un format qui se répète jusqu'à ce qu'une condition (entrée de saut de ligne vide) soit satisfaite.
readln (line)
est la partie qui reçoit l'entrée de caractères.
Seule la première fois est spéciale et conserve la valeur saisie dans prevItemPtr
. new (prevItemPtr)
est une instruction pour allouer une zone dans la mémoire du tas, y stocke la valeur numérique d'entrée et la sauvegarde dans le résultat (result
).
A partir de la deuxième fois, la valeur saisie est conservée dans currItemPtr
. Ensuite, la valeur numérique saisie est stockée et référencée à partir du précédent prevItemPtr
. Enfin, mettez à jour currItemPtr
en tant que nouveau prevItemPtr
.
Vient ensuite la fonction sumOfLines
qui calcule l'addition.
function sumOfLines(lines: ItemPtr): Integer;
var
a : Integer;
v : Integer;
err: Boolean;
begin
a := 0;
while lines <> nil do
begin
err := false;
v := toInt(lines^.text, err);
if not err then
a := a + v;
lines := lines^.next;
end;
sumOfLines := a;
end;
Cela convertit la chaîne en un entier et les ajoute tout en vérifiant les lignes ligne par ligne. La fonction toInt qui convertit une chaîne de caractères en entier est également définie dans le programme car elle n'est pas dans le Pascal standard. De plus, lorsqu'une chaîne de caractères autre qu'une valeur numérique arrive, elle est jugée par le deuxième argument err afin de ne pas provoquer d'erreur. (Par conséquent, ce deuxième argument est un appel de référence)
L'ensemble du programme ressemble à ceci:
program CalcSumApp;
type
ItemPtr = ^Item;
Item =
record
next: ItemPtr;
text: ShortString;
end;
function toInt(str: ShortString; var err: Boolean): Integer;
var
i : Integer;
n : Integer;
r : Integer;
begin
r := 0;
for i := 1 to ord(str[0]) do
begin
n := Ord(str[i]);
if (n < Ord('0')) or (n > Ord('9')) then
begin
err := true;
break;
end;
r := r * 10 + n - Ord('0');
end;
toInt := r;
end;
function toStr(n: Integer): ShortString;
var
m : Integer;
s : ShortString;
begin
s := '';
while n > 0 do
begin
m := n mod 10;
n := n div 10;
s := chr(m + ord('0')) + s;
end;
if s = '' then
s := '0';
toStr := s;
end;
procedure disposeAll(ptr: ItemPtr);
begin
if ptr <> nil then
begin
disposeAll(ptr^.next);
end;
end;
function readLines(): ItemPtr;
var
result: ItemPtr;
prevItemPtr: ItemPtr;
currItemPtr: ItemPtr;
line: ShortString;
begin
result := nil;
prevItemPtr := nil;
while true do
begin
write('>');
readln(line);
if line <> '' then
begin
if prevItemPtr = nil then
begin
new(prevItemPtr);
prevItemPtr^.text := line;
prevItemPtr^.next := nil;
result := prevItemPtr;
end
else
begin
new(currItemPtr);
currItemPtr^.text := line;
currItemPtr^.next := nil;
prevItemPtr^.next := currItemPtr;
prevItemPtr := currItemPtr;
end;
end
else
break;
end;
readLines := result;
end;
function sumOfLines(lines: ItemPtr): Integer;
var
a : Integer;
v : Integer;
err: Boolean;
begin
a := 0;
while lines <> nil do
begin
err := false;
v := toInt(lines^.text, err);
if not err then
a := a + v;
lines := lines^.next;
end;
sumOfLines := a;
end;
var
lines : ItemPtr;
sum : Integer;
begin
writeln('Veuillez saisir le numéro. Il se termine par une ligne vierge.');
lines := readLines();
sum := sumOfLines(lines);
writeln('Le total est' + toStr(sum) + 'est.');
disposeAll(lines);
end.
En Java, implémentons-le avec deux classes, la classe CalcSumApp
et la classe CalcSum
. Le premier est la classe CalcSumApp
pour l'ensemble de l'application.
public class CalcSumApp {
public static void main(String args[]) {
CalcSum cs = new CalcSum();
CalcSumApp app = new CalcSumApp();
app.start(cs);
}
public void start(CalcSum cs) {
System.out.println("Veuillez saisir le numéro. Il se termine par une ligne vierge.");
cs.readLines();
int sum = cs.getSum();
System.out.println("Le total est" + String.valueOf(sum) + "est.");
}
}
Dans la fonction main
, créez une instance de la classe CalcSumApp
et de la classe calcSum
et appelez la méthode start
.
Le contenu de la méthode start
est presque le même que celui de la routine principale de Pascal, mais ils ne gèrent pas directement les données, ils n'opèrent que des objets de la classe CalcSum
.
Ensuite, la classe CalcSum
ressemble à ceci:
class CalcSum {
List<String> list;
CalcSum () {
list = new ArrayList<String>();
}
public void readLines() {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String line;
try {
do {
System.out.print('>');
line = input.readLine();
if ("".equals(line)) {
break;
}
list.add(line);
} while (true);
} catch (IOException e) {
}
}
public int getSum() {
int sum = 0;
for (String s : list) {
try {
sum += Integer.valueOf(s).intValue();
} catch (NumberFormatException e) {
}
}
return sum;
}
}
La méthode readLines
qui accepte les entrées de la console et la méthode getSum
qui calcule la somme sont définies. Le contenu est presque le même que la fonction readLines
et la fonction sumOfLines
dans pascal. Cependant, les données numériques saisies (liste) sont conservées en tant que variable d'instance de la classe CalcSum
.
L'ensemble du programme ressemble à ceci: Comparé à Pascal, il est plus propre car il n'y a pas de processus de conversion tel que toInt ou toStr, ou de processus tel que disposeAll of memory release, et c'est bien que la liste soit contenue dans CalcSum. (Bien qu'il puisse être préférable de le rendre `privé ')
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.NumberFormatException;
class CalcSum {
List<String> list;
CalcSum () {
list = new ArrayList<String>();
}
public void readLines() {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String line;
try {
do {
System.out.print('>');
line = input.readLine();
if ("".equals(line)) {
break;
}
list.add(line);
} while (true);
} catch (IOException e) {
}
}
public int getSum() {
int sum = 0;
for (String s : list) {
try {
sum += Integer.valueOf(s).intValue();
} catch (NumberFormatException e) {
}
}
return sum;
}
}
public class CalcSumApp {
public static void main(String args[]) {
CalcSum cs = new CalcSum();
CalcSumApp app = new CalcSumApp();
app.start(cs);
}
public void start(CalcSum cs) {
System.out.println("Veuillez saisir le numéro. Il se termine par une ligne vierge.");
cs.readLines();
int sum = cs.getSum();
System.out.println("Le total est" + String.valueOf(sum) + "est.");
}
}
Comme c'est facile dans cet exemple, je pense qu'il est possible d'implémenter CalcSum comme un objet immuable, mais en général, dans la programmation orientée objet, je pense que les objets qui peuvent être immuables sont limités, donc je n'aborderai pas l'implémentation qui le rend immuable. fait.
Chez Haskell, la routine principale est à peu près la même et ressemble à ceci:
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Veuillez saisir le numéro. Il se termine par une ligne vierge."
lines <- readLines getLines
list <- sequence lines
let s = sum list
putStrLn $ "Le total est" ++ (show s) ++ "est."
La partie hSetBuffering stdout NoBuffering
, comme indiqué dans les commentaires de cet article, était que sans elle, la mise en mémoire tampon ne garantirait pas que les entrées et les sorties seraient dans l'ordre attendu. (Merci à @igrep)
getLines
est une liste qui reçoit un nombre infini de chaînes et est définie comme suit.
getLines :: [IO String]
getLines = do
repeat $ putStr ">" >> getLine
repeat
crée une liste infinie. En d'autres termes
repeat x = [x x x x .... x ... ]
C'est comme ça. C'est une caractéristique du traitement des délais de Hakell qu'une telle liste infinie peut être gérée, et je voulais l'utiliser comme ça.
readLines
est le processus de découpe de la pièce jusqu'à ce qu'une ligne vide soit entrée, et se présente comme suit.
readLines :: [IO String] -> IO [IO Int]
readLines [] = return []
readLines (l:ls) = do
v <- l :: IO String
if v == ""
then return []
else do
ll <- readLines ls
case readMaybe v of
Just x -> return $ (return x):ll
Nothing -> return ll
Si le premier élément du tableau est un caractère vide, c'est tout. Autrement,
Convertissez le premier élément en une valeur numérique (readMaybe
), et s'il peut être converti, ajoutez le converti (retroprocédé) et renvoyez-le, et s'il ne peut pas être converti, convertissez le reste (traité récursivement) Est de retour seulement.
list <- sequence lines
C'est un peu déroutant ici, mais je convertis un tableau IO en un tableau IO et j'en extrait son contenu. Ainsi, «list» n'est qu'un tableau de nombres.
À l'origine ici
list <- mapM (>>= return) lines
Cependant, comme indiqué dans le commentaire, il peut être résolu en un seul coup avec squence
, donc je l'ai changé.
Et le calcul du total se fait ensuite.
let s = sum list
(Je vais omettre l'histoire de l'évaluation parce que ma compréhension était erronée.)
L'ensemble du programme ressemble à ceci: La conversion des monades est un peu gênante, et contrairement à Java, la variable lines
est gérée directement, mais en utilisant la syntaxe do, elle peut être décrite comme pascal.
module CalcSumApp where
import Text.Read
import System.IO
getLines :: [IO String]
getLines = do
repeat $ putStr ">" >> getLine
readLines :: [IO String] -> IO [IO Int]
readLines [] = return []
readLines (l:ls) = do
v <- l :: IO String
if v == ""
then return []
else do
ll <- readLines ls
case readMaybe v of
Just x -> return $ (return x):ll
Nothing -> return ll
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Veuillez saisir le numéro. Il se termine par une ligne vierge."
lines <- readLines getLines
list <- sequence lines
let s = sum list
putStrLn $ "Le total est" ++ (show s) ++ "est."
Haskell a également essayé de créer un type de données et de l'encapsuler de manière orientée objet. Je ne pense pas que ce soit pratique, mais ...
La routine principale est
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Veuillez saisir le numéro. Il se termine par une ligne vierge."
calcSum <- newCalcSum -- calcSum = new CalcSum()
inputStrings calcSum -- calcSum.inputString()
sum <- getSum calcSum -- sum = calcSum.getSum()
putStrLn $ "Le total est" ++ (show sum) ++ "est."
Cela ressemble à ceci, et je pense que c'est plus proche de Java. Dans tout le code
module CalcSumApp2 where
import Text.Read
import Control.Monad
import Data.IORef
import Data.Maybe
import System.IO
data CalcSum = CalcSum { getStrings :: IORef [String] }
newCalcSum :: IO CalcSum
newCalcSum = do
ref <- newIORef []
return $ CalcSum ref
inputStrings :: CalcSum -> IO ()
inputStrings calcSum = do
let truncateStrings :: [IO String] -> IO [String]
truncateStrings [] = return []
truncateStrings (x:xs) = do
s <- x
if s == ""
then
return []
else do
l <- truncateStrings xs
return $ (:l) $! s -- strict evaluation of s corresponding to `return (s:l)`
list <- truncateStrings $ repeat $ putStr ">" >> getLine
writeIORef (getStrings calcSum) list -- calcSums.strings = list
getSum :: CalcSum -> IO Int
getSum calcSum = do
list <- readIORef (getStrings calcSum) :: IO [String] -- list = calcSum.strings
let nums = catMaybes $ readMaybe <$> list :: [Int]
return $ sum nums
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Veuillez saisir le numéro. Il se termine par une ligne vierge."
calcSum <- newCalcSum -- calcSum = new CalcSum()
inputStrings calcSum -- calcSum.inputString()
sum <- getSum calcSum -- sum = calcSum.getSum()
putStrLn $ "Le total est" ++ (show sum) ++ "est."
Ce que je voulais dire ici, c'est que l'objet (comme la chose) est ʻIORef. Je pense que les objets n'ont rien à voir avec IO en premier lieu, mais dans mon esprit, les objets sont des choses qui allouent une zone dans la mémoire du tas pour lire et écrire (une image comme DB) séparément de l'application, donc ʻIORef
Je pensais que ça viendrait bien.
Les objets dans les langages orientés objet ne sont pas seulement de type IORef (partagés et modifiables), mais également des objets non partagés, des objets immuables, des objets immuables et sans côtés (comme aucune entrée / sortie IO) et des effets secondaires. Je pense qu'il y a diverses choses telles que des objets volumineux (objets qui enveloppent le traitement qui interagit avec le serveur externe), mais je ne pouvais pas bien y penser, donc je vais simplement présenter cet exemple ici. Je le garderai.
Le Pascal standard a peu de fonctionnalités et vous devez implémenter vous-même toInt
ou toStr
, ou vous devez libérer la mémoire vous-même, mais ce processus (lourd) est fait. Quand je l'écris, j'aime me sentir comme "Oh, je programme".
J'ai l'impression de jouer avec une voiture manuelle au lieu d'une voiture automatique. Ces jours-ci, Go est comme ça.
Bien que Java ait été critiqué de diverses manières, je pense que c'est un bon langage facile à utiliser. Je pense que le langage de programmation orienté objet est plus adapté à la méta-programmation comme Ruby et à la création dynamique d'objets comme JavaScript, mais il y a un sentiment de sécurité que la classe est déterminée statiquement. Ça m'a l'air bien. J'aime Java 1.5 ou plus, et c'est dommage que cela se complique avec tant d'autres paradigmes.
Haskell a beaucoup à étudier et a du mal, mais je suis très heureux quand cela fonctionne. Il y a beaucoup de fonctions et c'est difficile à comprendre et à retenir, mais on a l'impression que c'est un gros problème quand on sait comment l'utiliser, et c'est bien d'avoir une solution statique. Je pense qu'il est bon de faire attention au type.
Dans cet exemple simple, cela ne change pas grand-chose dans aucune implémentation, mais je me suis demandé s'il y avait encore des fonctionnalités de chaque langage.
Le nombre de langages de programmation de nos jours est multi-paradigme, c'est pourquoi je pense qu'il est important d'apprendre les bases des langages à paradigme unique.
Si vous avez des erreurs ou des insuffisances, veuillez commenter.
Recommended Posts